C ++ new int [0] - nó sẽ phân bổ bộ nhớ?


240

Một ứng dụng thử nghiệm đơn giản:

cout << new int[0] << endl;

đầu ra:

0x876c0b8

Vì vậy, nó trông giống như nó hoạt động. Tiêu chuẩn nói gì về điều này? Có phải luôn luôn hợp pháp để "phân bổ" khối bộ nhớ trống?


4
+1 Câu hỏi rất thú vị - mặc dù tôi không chắc nó quan trọng đến mức nào trong mã thực.
Zifre

37
@Zifre: Tôi yêu cầu sự tò mò, nhưng điều đó có thể quan trọng trong thế giới thực, ví dụ: khi kích thước của các khối bộ nhớ được phân bổ được tính theo một cách nào đó và kết quả của phép tính có thể bằng 0, thì không cần thêm ngoại lệ để không phân bổ các khối có kích thước bằng 0 .. Bởi vì chúng nên được phân bổ và xóa mà không có lỗi (nếu chỉ khối có kích thước bằng 0 không bị hủy đăng ký). Vì vậy, nói chung điều này mang lại sự trừu tượng rộng hơn về khối bộ nhớ là gì.

2
@ emg-2: Trong tình huống ví dụ của bạn, điều đó thực sự không quan trọng, vì xóa [] là hoàn toàn hợp pháp trên một con trỏ NULL :-).
Evan Teran

2
Nó chỉ liên quan một cách hữu hình - vì vậy tôi đang bình luận ở đây - nhưng C ++ theo nhiều cách đảm bảo rằng các đối tượng riêng biệt có địa chỉ duy nhất ... ngay cả khi chúng không yêu cầu lưu trữ rõ ràng. Một thí nghiệm liên quan sẽ là kiểm tra kích thước của một cấu trúc trống. Hoặc một mảng của cấu trúc đó.
Drew Dormann

2
Để giải thích về nhận xét của Shmoopty: Đặc biệt là khi lập trình với các mẫu (ví dụ: các mẫu lớp chính sách như std :: allocator), thông thường trong C ++ có các đối tượng có kích thước bằng không. Mã chung có thể cần phân bổ động các đối tượng đó và sử dụng các con trỏ tới chúng để so sánh danh tính đối tượng. Đây là lý do tại sao toán tử new () trả về các con trỏ duy nhất cho các yêu cầu có kích thước bằng không. Mặc dù ít quan trọng / phổ biến hơn, nhưng lý do tương tự áp dụng cho phân bổ mảng và toán tử mới [] ().
Trevor Robinson

Câu trả lời:


233

Từ 5.3.4 / 7

Khi giá trị của biểu thức trong một khai báo trực tiếp mới bằng 0, hàm phân bổ được gọi để phân bổ một mảng không có phần tử.

Từ 3.7.3.1/2

Hiệu quả của việc hủy bỏ hội nghị một con trỏ được trả về như một yêu cầu cho kích thước bằng 0 là không xác định.

Cũng thế

Ngay cả khi kích thước của không gian được yêu cầu [bởi new] bằng 0, yêu cầu có thể thất bại.

Điều đó có nghĩa là bạn có thể làm điều đó, nhưng bạn không thể hợp pháp (theo cách được xác định rõ ràng trên tất cả các nền tảng) theo quy định bộ nhớ mà bạn nhận được - bạn chỉ có thể chuyển nó sang xóa mảng - và bạn nên xóa nó.

Dưới đây là một ghi chú thú vị (nghĩa là không phải là một phần quy định của tiêu chuẩn, nhưng được bao gồm cho mục đích lưu trữ) được đính kèm với câu từ 3.7.3.1/2

[32. Mục đích là để toán tử new () có thể thực hiện được bằng cách gọi malloc () hoặc calloc (), vì vậy các quy tắc về cơ bản là giống nhau. C ++ khác với C khi yêu cầu số không yêu cầu trả về con trỏ không null.]


Tôi bị rò rỉ bộ nhớ nếu tôi không xóa. Nó có được mong đợi không? Ít nhất tôi đã không mong đợi.
EralpB

12
@EralpB: ngược lại, ngay cả khi yêu cầu của bạn bằng 0, việc phân bổ này xảy ra trên Heap, trong đó một yêu cầu ngụ ý một số hoạt động giữ sách, như phân bổ và khởi tạo các nhân viên bảo vệ heap trước và sau khu vực do người cấp phát đưa vào, chèn vào người tự do hoặc cấu trúc khủng khiếp phức tạp khác. Giải phóng nó có nghĩa là làm sổ sách lạc hậu.
v.oddou

3
@EralpB vâng tôi đoán bạn có thể mong đợi rò rỉ bộ nhớ mỗi khi bạn không cân bằng a new[]với delete[]bất kỳ kích thước nào. Đặc biệt, khi bạn gọi, new[i]bạn cần thêm một chút bộ nhớ so với mức mà bạn đang cố gắng phân bổ để lưu trữ kích thước của mảng (sau này được sử dụng delete[]khi xử lý)
pqnet

24

Có, việc phân bổ một mảng có kích thước bằng 0 như thế này là hợp pháp. Nhưng bạn cũng phải xóa nó.


1
Bạn có một trích dẫn cho điều này? Chúng ta đều biết đó int ar[0];là bất hợp pháp tại sao mới OK?
Motti

4
Thật thú vị khi C ++ không quá mạnh với việc cấm các đối tượng có kích thước bằng không. Hãy nghĩ về tối ưu hóa lớp cơ sở trống: Ở đây cũng vậy, một đối tượng phụ của lớp cơ sở trống có thể có kích thước bằng không. Ngược lại, Tiêu chuẩn C nỗ lực mạnh mẽ để đảm bảo không bao giờ có các đối tượng có kích thước bằng 0 được tạo ra: Trong khi xác định malloc (0), nó cho biết hiệu ứng của việc chạy malloc với một số đối số khác không, chẳng hạn. Và trong cấu trúc {...; T n []; }; khi không có không gian được phân bổ cho mảng (FAM), nó nói rằng nó hoạt động như thể "n" có một phần tử. (Trong cả hai trường hợp, sử dụng đối tượng theo bất kỳ cách nào là UB, như xn [0])
Johannes Schaub - litb

1
Tôi nghĩ rằng sizeof (type)sẽ không bao giờ trở lại số không. Xem ví dụ: stackoverflow.com/questions/2632021/can-sizeof-return-0-zero
pqnet

Ngay cả cái gọi là "tối ưu hóa lớp cơ sở trống" chỉ có liên quan vì một sự khăng khăng rằng tất cả các đối tượng (trái ngược với tất cả các byte trong các đối tượng) phải có địa chỉ duy nhất. C ++ có thể đã được làm đơn giản hơn nếu mã thực sự quan tâm đến các đối tượng có địa chỉ duy nhất được yêu cầu để đảm bảo rằng chúng có kích thước khác không.
supercat

15

Tiêu chuẩn nói gì về điều này? Có phải luôn luôn hợp pháp để "phân bổ" khối bộ nhớ trống?

Mỗi đối tượng có một danh tính duy nhất, tức là một địa chỉ duy nhất, ngụ ý độ dài khác không (lượng bộ nhớ thực tế sẽ được tăng âm thầm, nếu bạn yêu cầu byte không).

Nếu bạn đã phân bổ nhiều hơn một trong những đối tượng này thì bạn sẽ thấy chúng có địa chỉ khác nhau.


"Mỗi đối tượng có một danh tính duy nhất, tức là một địa chỉ duy nhất, ngụ ý độ dài khác không" - đúng, nhưng sai. Đối tượng có địa chỉ, nhưng con trỏ tới đối tượng có thể trỏ đến bộ nhớ ngẫu nhiên. "Dung lượng bộ nhớ thực tế sẽ âm thầm tăng lên, nếu bạn yêu cầu 0 byte" - không chắc chắn. operator []cũng lưu trữ kích thước của mảng ở đâu đó (xem isocpp.org/wiki/faq/freestore-mgmt#num-elems-in-new-array ). Vì vậy, nếu việc triển khai phân bổ số byte bằng dữ liệu, thì nó chỉ có thể phân bổ cho số byte và 0 byte cho dữ liệu, trả về con trỏ 1 quá khứ.
Steed

Độ dài khác không của bộ nhớ được phân bổ, không phải là độ dài khác không của bộ nhớ có thể sử dụng. Quan điểm của tôi là hai con trỏ tới hai đối tượng riêng biệt không nên trỏ đến cùng một địa chỉ.
ChrisW

1
Tiêu chuẩn ( open-std.org/jtc1/sc22/wg21/docs/ con / 2012 / n3337.pdf ) thực sự nói (3.7.4.1/2) rằng các lệnh gọi khác nhau operator new[]sẽ trả về các con trỏ khác nhau. BTW, new expressioncó một số quy tắc bổ sung (5.3.4). Tôi không thể tìm thấy bất kỳ manh mối nào newvới kích thước 0 thực sự cần thiết để phân bổ bất cứ thứ gì. Xin lỗi, tôi đã từ chối vì tôi thấy rằng câu trả lời của bạn không trả lời các câu hỏi, nhưng đang cung cấp một số tuyên bố gây tranh cãi.
Steed

@ Lưu bộ nhớ để lưu chiều dài của khối có thể được tính toán ngầm bằng không gian địa chỉ. Ví dụ, đối với các mảng có kích thước bằng 0, việc new[]triển khai có thể trả về các địa chỉ trong phạm vi không có bộ nhớ động được ánh xạ, do đó không thực sự sử dụng bất kỳ bộ nhớ nào (trong khi sử dụng không gian địa chỉ)
pqnet

@pqnet: Việc triển khai chất lượng tốt sẽ cho phép không giới hạn số chu kỳ mới / xóa, có nên không? Trên nền tảng có con trỏ 64 bit, có thể hợp lý khi nói rằng một máy tính thực hiện while(!exitRequested) { char *p = new char[0]; delete [] p; }vòng lặp mà không có con trỏ tái chế sẽ sụp đổ thành bụi trước khi có thể hết không gian địa chỉ, nhưng trên nền tảng có con trỏ 32 bit sẽ là một giả định ít hợp lý.
supercat

14

Có nó là hoàn toàn hợp pháp để phân bổ một 0khối có kích thước với new. Bạn chỉ đơn giản là không thể làm bất cứ điều gì hữu ích với nó vì không có dữ liệu hợp lệ để bạn truy cập. int[0] = 5;Là bất hợp pháp.

Tuy nhiên, tôi tin rằng tiêu chuẩn cho phép những thứ như malloc(0)trở lạiNULL .

Bạn vẫn sẽ cần delete []bất cứ con trỏ nào bạn nhận được từ phân bổ là tốt.


5
Về malloc, bạn đã đúng - đó là việc thực hiện được xác định. Điều này thường được xem là một hành vi sai trái.

Tôi cho rằng một câu hỏi thú vị là: phiên bản mới nhất của NULL có thể trả lại được không nếu được cho kích thước bằng 0?
Evan Teran

1
Các ngữ nghĩa của mới và malloc không được liên kết theo bất kỳ cách nào, ít nhất là theo tiêu chuẩn.

không nói rằng họ ... đặc biệt hỏi về phiên bản mới.
Evan Teran

$ cách xung quanh) - xem chú thích tôi đưa vào câu trả lời của tôi
Faisal Vali

1

Thật kỳ lạ, C ++ yêu cầu toán tử mới trả về một con trỏ hợp pháp ngay cả khi không có byte nào được yêu cầu. (Yêu cầu hành vi nghe có vẻ kỳ quặc này đơn giản hóa mọi thứ ở nơi khác trong ngôn ngữ.)

Tôi thấy Phiên bản thứ ba hiệu quả của C ++ đã nói như thế này trong "Mục 51: Tuân thủ quy ước khi viết mới và xóa".


0

Tôi đảm bảo với bạn rằng int [0] mới khiến bạn tốn thêm dung lượng vì tôi đã thử nghiệm nó.

Ví dụ: việc sử dụng bộ nhớ của

int **arr = new int*[1000000000];

nhỏ hơn đáng kể so với

int **arr = new int*[1000000000];
for(int i =0; i < 1000000000; i++) {
    arr[i]=new int[0];
}

Việc sử dụng bộ nhớ của đoạn mã thứ hai trừ đi đoạn mã thứ nhất là bộ nhớ được sử dụng cho nhiều int mới [0].

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.