Điểm của malloc (0) là gì?


Câu trả lời:


121

Theo các thông số kỹ thuật, malloc (0) sẽ trả về "một con trỏ null hoặc một con trỏ duy nhất có thể được chuyển thành công đến free ()".

Về cơ bản, điều này cho phép bạn không phân bổ gì, nhưng vẫn chuyển biến "artist" cho một cuộc gọi đến free () mà không cần lo lắng. Đối với các mục đích thực tế, nó khá giống với việc làm:

artist = NULL;

51
Cá nhân tôi nghĩ đặt thành NULL là một chiến lược đa nền tảng tốt hơn, vì free () được đảm bảo (theo thông số kỹ thuật) hoạt động tốt trên NULL làm đầu vào.
Reed Copsey

2
Như đã đề cập bởi C. Ross, một số nền tảng, về mặt kỹ thuật, có thể trả về một con trỏ ở đây (đó là "con trỏ duy nhất có thể được chuyển sang miễn phí"), nhưng nếu bạn đang coi đây là một ký tự *, điều đó có thể cung cấp cho bạn char không hợp lệ, không kết thúc. Sẽ rất nguy hiểm nếu dựa vào điều này trong các tình huống đa nền tảng.
Reed Copsey

9
Thực sự muốn số kỹ thuật có thể nói "một cách an toàn thông qua với realloc" cũng -.-
hanshenrik

1
@NSAddict "cấu trúc trống trong đó sizeof sẽ trả về 0", vui lòng cung cấp một ví dụ, giống như một phần mở rộng ngôn ngữ.
Chux - Khôi phục Monica

1
@hanshenrik Ai nói rằng bạn không thể? realloc()cho phép bạn chuyển bất kỳ con trỏ hợp lệ nào được trả về malloc(). Nên là đủ.
glglgl

51

Tiêu chuẩn C (C17 7.22.3 / 1) cho biết:

Nếu kích thước của không gian được yêu cầu bằng 0, hành vi được thực hiện được xác định: con trỏ null được trả về hoặc hành vi giống như kích thước là một số giá trị khác không, ngoại trừ con trỏ trả về sẽ không được sử dụng để truy cập một đối tượng.

Vì vậy, malloc(0)có thể trả về NULLhoặc một con trỏ hợp lệ có thể không được tham chiếu . Trong cả hai trường hợp, hoàn toàn hợp lệ để gọi free()nó.

Tôi không thực sự nghĩ rằng nó malloc(0)có nhiều công dụng, ngoại trừ những trường hợp malloc(n)được gọi trong một vòng lặp chẳng hạn, và ncó thể bằng không.

Nhìn vào đoạn mã trong liên kết, tôi tin rằng tác giả đã có hai quan niệm sai lầm:

  • malloc(0)luôn luôn trả về một con trỏ hợp lệ và
  • free(0) là xấu.

Vì vậy, ông đảm bảo rằng artistvà các biến khác luôn có một số giá trị "hợp lệ" trong chúng. Các bình luận nói càng nhiều: // these must always point at malloc'd data.


10
Thực tế là nó phụ thuộc vào việc triển khai làm cho nó ít nhiều hoàn toàn vô dụng - đây là một trong những điểm khó khăn hơn của tiêu chuẩn C và khá nhiều ủy ban tiêu chuẩn (ví dụ PJ Plauger) đã phàn nàn về nó.

10
Tôi đồng ý. Nếu malloc(0)trả về một con trỏ hợp lệ, thì malloc()trả về luôn NULLcó nghĩa là "thất bại" và 0không phải là trường hợp đặc biệt nữa, điều này nhất quán hơn.
Alok Singhal

1
Vì các trường hợp mallockhông lấy được bộ nhớ được xác định bởi việc triển khai, một triển khai có thể đơn giản xác định rằng phân bổ kích thước-0 luôn không thỏa mãn ( ENOMEM) và bây giờ malloc(0)trả về 0 (với errno==ENOMEM) là nhất quán. :-)
R .. GitHub NGỪNG TRỢ GIÚP LÚC NÀY

6
Bạn có thể realloctrả lại một con trỏ malloc(0)không? Bạn có thể realloc((char*)NULL)?
Braden Best

3
@Braden Tốt nhất Có cho cả hai.
Chux - Khôi phục Monica

11

hành vi malloc (0) là triển khai cụ thể. Thư viện có thể trả về NULL hoặc có hành vi malloc thông thường, không được cấp phát bộ nhớ. Bất cứ điều gì nó làm, nó phải được ghi lại ở đâu đó.

Thông thường, nó trả về một con trỏ hợp lệ và duy nhất nhưng KHÔNG được tham chiếu. Cũng lưu ý rằng nó CÓ THỂ sử dụng bộ nhớ mặc dù nó không thực sự cấp phát bất cứ thứ gì.

Có thể phân bổ lại một con trỏ malloc (0) không null.

Mặc dù vậy, có một nguyên văn malloc (0) không được sử dụng nhiều. Nó chủ yếu được sử dụng khi phân bổ động là byte 0 và bạn không quan tâm đến việc xác thực nó.


9
malloc()phải giữ "thông tin quản lý" ở đâu đó (ví dụ như kích thước của khối được phân bổ và dữ liệu phụ trợ khác). Vì vậy, nếu malloc(0)không trả về NULL, nó sẽ sử dụng bộ nhớ để lưu trữ thông tin đó, và nếu không phải là free()d, sẽ tạo thành rò rỉ bộ nhớ.
Alok Singhal

Các triển khai Malloc thực hiện việc lưu trữ hồ sơ có thể thêm một lượng dữ liệu nhất định trên mỗi con trỏ được trả về trên kích thước được yêu cầu.
user7116

1
Bộ nhớ được tiêu thụ và bộ nhớ được cấp phát không có nghĩa giống nhau. Trong trường hợp này, hầu hết việc triển khai sẽ trả về một con trỏ duy nhất. Điều này có nghĩa là một phần của không gian địa chỉ cần được hy sinh cho con trỏ đó. Tùy thuộc vào trình cấp phát, điều này thực sự có nghĩa là nó sẽ cấp phát 1 byte hoặc nhiều hơn.
Coincoin

1
Thư viện có thể làm bất cứ điều gì nó muốn - tốt, nó có thể trả về một con trỏ duy nhất mà không có con trỏ nào khác malloc()sẽ trả lại hoặc trả về NULL.
Alok Singhal

1
@jldupont: Ít nhất thì thư viện Microsoft C Run-Time trả về một con trỏ duy nhất cho malloc(0). Tuy nhiên, trong cùng việc triển khai thư viện C chuẩn, realloc(ptr, 0)giải phóng ptrvà trả về NULL.
Medinoc

5

Có một câu trả lời ở nơi khác trên trang này bắt đầu "malloc (0) sẽ trả về một địa chỉ bộ nhớ hợp lệ và phạm vi của nó sẽ phụ thuộc vào loại con trỏ đang được cấp phát bộ nhớ". Tuyên bố này không chính xác (Tôi không có đủ uy tín để bình luận trực tiếp về câu trả lời đó, vì vậy không thể đặt bình luận này trực tiếp dưới đó).

Thực hiện malloc (0) sẽ không tự động cấp phát bộ nhớ có kích thước chính xác. Hàm malloc không biết bạn đang truyền kết quả cho nó. Hàm malloc hoàn toàn dựa vào số kích thước mà bạn cung cấp làm đối số của nó. Bạn cần thực hiện malloc (sizeof (int)) để có đủ dung lượng lưu trữ để giữ một int, chẳng hạn như 0.


4

malloc(0)không có ý nghĩa gì đối với tôi, trừ khi mã dựa trên hành vi cụ thể để triển khai. Nếu mã có nghĩa là di động, thì nó phải tính đến thực tế là trả về NULL malloc(0)không phải là một thất bại. Vì vậy, tại sao không chỉ định NULL cho artistdù sao, vì đó là một kết quả thành công hợp lệ và ít mã hơn, và sẽ không khiến các lập trình viên bảo trì của bạn mất thời gian tìm ra nó?

malloc(SOME_CONSTANT_THAT_MIGHT_BE_ZERO)hoặc malloc(some_variable_which_might_be_zero)có lẽ có thể có công dụng của chúng, mặc dù một lần nữa bạn phải hết sức cẩn thận để không coi trả về NULL là lỗi nếu giá trị là 0, nhưng kích thước 0 được cho là OK.


3

Có rất nhiều câu trả lời đúng một nửa ở đây, vì vậy đây là sự thật khó. Trang man cho malloc()nói:

Nếu kích thước là 0, thì hàm malloc () trả về giá trị NULL hoặc một giá trị con trỏ duy nhất mà sau này có thể được chuyển thành công tới free ().

Điều đó có nghĩa là, hoàn toàn không có gì đảm bảo rằng kết quả của malloc(0)là duy nhất hoặc không phải là NULL. Đảm bảo duy nhất được cung cấp bởi định nghĩa của free(), một lần nữa, đây là những gì trang người dùng nói:

Nếu ptr là NULL, không có thao tác nào được thực hiện.

Vì vậy, bất cứ điều gì malloc(0)trả lại, nó có thể được chuyển đến một cách an toàn free(). Nhưng một NULLcon trỏ cũng vậy.

Do đó, viết artist = malloc(0); không có cách nào tốt hơn viết artist = NULL;


1
Quá tệ, việc triển khai không được phép trả về một con trỏ không phải null, không phải là duy nhất. Bằng cách đó, malloc(0)có thể trả về, chẳng hạn, 0x1, và free()có thể kiểm tra trường hợp đặc biệt của 0x1 giống như đối với 0x0.
Todd Lehman

3
@Todd Lehman Một triển khai có thể làm như bạn đề xuất. Thông số C không chỉ định kết quả phải là " NULL, hoặc một con trỏ duy nhất". thay vào đó là 'con trỏ null hoặc con trỏ tới không gian được cấp phát ". Không có yêu cầu duy nhất . OTOH, việc trả về giá trị đặc biệt không phải là duy nhất có thể làm gián đoạn mã tính trên các giá trị duy nhất. Có lẽ là một câu hỏi dạng chữ hoa cho SO.
chux - Phục hồi Monica

mancũng có thể ghi lại biểu mẫu do triển khai xác định được sử dụng trong * nix. Trong trường hợp này không, nhưng nó vẫn không phải là một nguồn kinh điển cho C. chung
Lundin

@Lundin Đúng. Nhưng man-page dễ tiếp cận hơn nhiều so với tiêu chuẩn C, và man-page trên các hệ thống GNU / Linux thường ghi lại khá tốt (các) tiêu chuẩn mà việc triển khai tuân theo. Cùng với thông tin về các bộ phận tuân theo tiêu chuẩn nào, chúng có khác nhau không. Tôi có cảm giác rằng cả hai đều muốn chính xác và quảng cáo từng bit đó là một tiện ích mở rộng GNU ...
cmaster - phục hồi monica

3

Tại sao bạn không nên làm điều này ...

Vì giá trị trả về của malloc phụ thuộc vào việc triển khai, bạn có thể lấy lại con trỏ NULL hoặc một số địa chỉ khác. Điều này có thể dẫn đến việc tràn bộ đệm heap nếu mã xử lý lỗi không kiểm tra cả kích thước và giá trị trả về, dẫn đến các vấn đề ổn định (sự cố) hoặc các vấn đề bảo mật thậm chí tồi tệ hơn.

Hãy xem xét ví dụ này, trong đó việc truy cập thêm vào bộ nhớ thông qua địa chỉ trả về sẽ làm hỏng kích thước heap iff bằng 0 và việc triển khai trả về giá trị không phải NULL.

size_t size;

/* Initialize size, possibly by user-controlled input */

int *list = (int *)malloc(size);
if (list == NULL) {
  /* Handle allocation error */
}
else {
  /* Continue processing list */
}

Xem trang Mã hóa Bảo mật này từ Tiêu chuẩn Mã hóa CERT mà tôi đã lấy ví dụ ở trên để đọc thêm.


Liên kết đã được di chuyển: wiki.sei.cmu.edu/confluence/display/c/…
Cacahuete Frito

2

Phải thừa nhận rằng tôi chưa bao giờ thấy điều này trước đây, đây là lần đầu tiên tôi thấy cú pháp này, có thể nói, một trường hợp cổ điển của hàm quá mức cần thiết. Kết hợp với câu trả lời của Reed, tôi muốn chỉ ra rằng có một thứ tương tự, xuất hiện giống như một hàm quá tải realloc:

  • foo không phải là NULL và kích thước bằng 0 realloc(foo, size);,. Khi bạn chuyển một con trỏ không phải NULL và kích thước bằng 0 vào realloc, realloc hoạt động như thể bạn đã gọi là free (…)
  • foo là NULL và kích thước khác 0 và lớn hơn 1 , realloc(foo, size);. Khi bạn truyền vào một con trỏ NULL và kích thước là khác 0, realloc hoạt động như thể bạn đã gọi malloc (…)

Hy vọng điều này sẽ giúp, Trân trọng, Tom.


1

Để thực sự trả lời câu hỏi được đặt ra: không có lý do gì để làm điều đó


0

malloc (0) sẽ trả về NULL hoặc một con trỏ hợp lệ có thể được chuyển đúng sang miễn phí. Và mặc dù có vẻ như bộ nhớ mà nó trỏ đến là vô dụng hoặc nó không thể được ghi vào hoặc đọc từ đó, điều đó không phải lúc nào cũng đúng. :)

int *i = malloc(0);
*i = 100;
printf("%d", *i);

Chúng tôi mong đợi một lỗi phân đoạn ở đây, nhưng đáng ngạc nhiên, điều này in ra 100! Đó là bởi vì malloc thực sự yêu cầu một lượng lớn bộ nhớ khi chúng ta gọi malloc lần đầu tiên. Mọi cuộc gọi tới malloc sau đó, đều sử dụng bộ nhớ từ đoạn lớn đó. Chỉ sau khi phần lớn đó kết thúc, bộ nhớ mới được yêu cầu.

Sử dụng malloc (0): nếu bạn ở trong trường hợp bạn muốn các lệnh gọi malloc tiếp theo nhanh hơn, thì việc gọi malloc (0) sẽ thực hiện điều đó cho bạn (ngoại trừ trường hợp cạnh).


1
Việc viết thư tới *icó thể không gặp sự cố trong trường hợp của bạn, nhưng đó là hành vi không xác định. Hãy coi chừng những con quỷ mũi!
John Dvorak

1
Đúng. Điều đó đúng. Nó là thực hiện cụ thể. Tôi đã xác minh nó trên MaxOS X và một số bản phân phối Linux. Tôi đã không thử nó trên các nền tảng khác. Phải nói rằng, khái niệm mà tôi mô tả đã được mô tả trong cuốn sách "Ngôn ngữ lập trình C" của Brain Kernighan và Dennis Ritchie.
Sagar Bhosale

Tôi biết: bình luận siêu muộn về câu hỏi này. Nhưng đôi khi có một công dụng cho malloc(0)điều đó không được đề cập. Trên những triển khai mà nó trả về giá trị không phải NULL, đặc biệt là trong bản dựng GỠ LỖI, nó có khả năng phân bổ NHIỀU HƠN mức bạn yêu cầu và cung cấp cho bạn con trỏ để chỉ qua tiêu đề nội bộ của nó. Điều này cho phép bạn cảm nhận được việc sử dụng bộ nhớ thực tế nếu bạn có được điều này trước và sau một loạt phân bổ. ví dụ: void* before = malloc(0); ... void* after = malloc(0); long long total = after - before;hoặc một số như vậy.
Jesse Chisholm

Tôi đã đọc "Ngôn ngữ lập trình C" của Brain Kernighan và Dennis Ritchie và tôi không nhớ nó nói gì về điều đó malloc(0). Bạn có thể vui lòng cho biết bạn cũng đang tham khảo chương nào không? Cung cấp một báo giá chính xác cũng sẽ tốt.
Андрей Беньковский

0

Trong Windows:

  • void *p = malloc(0);sẽ cấp phát một bộ đệm có độ dài bằng không trên heap cục bộ. Con trỏ được trả về là một con trỏ heap hợp lệ.
  • malloccuối cùng là các cuộc gọi HeapAllocbằng cách sử dụng đống thời gian chạy C mặc định mà sau đó sẽ gọi RtlAllocateHeap, v.v.
  • free(p);sử dụng HeapFreeđể giải phóng bộ đệm có độ dài 0 trên heap. Không giải phóng nó sẽ dẫn đến rò rỉ bộ nhớ.

0

Nó thực sự khá hữu ích và (rõ ràng là IMHO), hành vi được phép trả về một con trỏ NULL đã bị hỏng. Một con trỏ động không chỉ hữu ích cho những gì nó trỏ đến, mà còn thực tế là địa chỉ của nó là duy nhất. Trả về NULL loại bỏ thuộc tính thứ hai. Tất cả các chương trình mallocs nhúng tôi (thực tế là khá thường xuyên) đều có hiện tượng này.



-2

Đây là phân tích sau khi chạy với công cụ kiểm tra bộ nhớ valgrind.

==16740== Command: ./malloc0
==16740==
p1 = 0x5204040
==16740==
==16740== HEAP SUMMARY:
==16740==     in use at exit: 0 bytes in 0 blocks
==16740==   total heap usage: 2 allocs, 2 frees, 1,024 bytes allocated
==16740==
==16740== All heap blocks were freed -- no leaks are possible

và đây là mã mẫu của tôi:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
   //int i;
   char *p1;

   p1 = (char *)malloc(0);
   printf("p1 = %p\n", p1);

   free(p1);

   return 0;

}

Theo mặc định 1024 byte được cấp phát. Nếu tôi tăng kích thước của malloc, các byte được phân bổ sẽ tăng thêm 1025, v.v.


-3

Theo câu trả lời của Reed Copsey và trang người đàn ông của malloc, tôi đã viết một số ví dụ để kiểm tra. Và tôi phát hiện ra rằng malloc (0) sẽ luôn cung cấp cho nó một giá trị duy nhất. Xem ví dụ của tôi:

char *ptr;
if( (ptr = (char *) malloc(0)) == NULL )
    puts("Got a null pointer");
else
    puts("Got a valid pointer");

Kết quả đầu ra sẽ là "Có một con trỏ hợp lệ", có nghĩa ptrlà không rỗng.


-6

malloc(0)sẽ trả về một địa chỉ bộ nhớ hợp lệ và phạm vi của nó sẽ phụ thuộc vào loại con trỏ đang được cấp phát bộ nhớ. Ngoài ra, bạn có thể gán giá trị cho vùng bộ nhớ nhưng điều này phải nằm trong phạm vi với loại con trỏ đang được sử dụng. Bạn cũng có thể giải phóng bộ nhớ được cấp phát. Tôi sẽ giải thích điều này bằng một ví dụ:

int *p=NULL;
p=(int *)malloc(0);
free(p);

Đoạn mã trên sẽ hoạt động tốt trong gcctrình biên dịch trên máy Linux. Nếu bạn có trình biên dịch 32 bit thì bạn có thể cung cấp các giá trị trong phạm vi số nguyên, tức là -2147483648 đến 2147483647. Tương tự cũng áp dụng cho các ký tự. Xin lưu ý rằng nếu kiểu con trỏ được khai báo bị thay đổi thì phạm vi giá trị sẽ thay đổi bất kể kiểu gõ nào malloc, tức là

unsigned char *p=NULL;
p =(char *)malloc(0);
free(p);

p sẽ nhận giá trị từ 0 đến 255 của char vì nó được khai báo là int không dấu.


5
Krellan đã đúng khi chỉ ra rằng câu trả lời này là sai: malloc()không biết gì về chất đúc (thực tế là hoàn toàn siêu lỏng trong C). Tham chiếu đến giá trị trả về của malloc(0)sẽ gọi hành vi không xác định.
cmaster - phục hồi monica

-6

Chỉ để sửa một ấn tượng sai ở đây:

artist = (char *) malloc(0);sẽ không bao giờ trở lại NULL; nó không giống như artist = NULL;. Viết một chương trình đơn giản và so sánh artistvới NULL. if (artist == NULL)là sai và if (artist)là đúng.

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.