Sự khác biệt giữa malloc và calloc?


780

Sự khác biệt giữa làm là gì:

ptr = (char **) malloc (MAXELEMS * sizeof(char *));

hoặc là:

ptr = (char **) calloc (MAXELEMS, sizeof(char*));

Khi nào nên sử dụng calloc trên malloc hoặc ngược lại?



8
Trong C, bạn có thể viết ở trên một cách khái quát hơn như sau:ptr = calloc(MAXELEMS, sizeof(*ptr));
chqrlie

7
Một bài viết thú vị về sự khác biệt giữa calloc và malloc + memset vorpus.org/blog/why-does-calloc-exist
ddddavidee

2
@ddddavidee Tôi cũng tìm thấy blog đó sau khi tôi không hài lòng với rất nhiều câu trả lời trên mạng. Nathaniel J. Smith xứng đáng được hơn 100 điểm SO cho phân tích của mình.
tuổi thọ

Câu trả lời:


851

calloc()cung cấp cho bạn bộ đệm khởi tạo bằng không, trong khi malloc()để lại bộ nhớ chưa được khởi tạo.

Đối với phân bổ lớn, hầu hết các calloctriển khai trong các HĐH chính sẽ nhận được các trang không có từ hệ điều hành (ví dụ: qua POSIX mmap(MAP_ANONYMOUS)hoặc Windows VirtualAlloc), do đó không cần phải viết chúng trong không gian người dùng. Đây là cách bình thường cũng mallocnhận được nhiều trang hơn từ HĐH; callocchỉ tận dụng sự bảo đảm của HĐH.

Điều này có nghĩa là callocbộ nhớ vẫn có thể được "sạch" và được phân bổ một cách lười biếng, và sao chép trên bản ghi được ánh xạ tới một trang vật lý được chia sẻ trên toàn hệ thống. (Giả sử một hệ thống có bộ nhớ ảo.)

Một số trình biên dịch thậm chí có thể tối ưu hóa malloc + memset (0) thành calloc cho bạn, nhưng bạn nên sử dụng calloc một cách rõ ràng nếu bạn muốn bộ nhớ đọc thành 0.

Nếu bạn chưa từng đọc bộ nhớ trước khi viết bộ nhớ, hãy sử dụng mallocđể nó có thể (có khả năng) cung cấp cho bạn bộ nhớ bẩn từ danh sách miễn phí nội bộ của nó thay vì nhận các trang mới từ HĐH. (Hoặc thay vì bỏ một khối bộ nhớ trong danh sách miễn phí để phân bổ nhỏ).


Việc triển khai được nhúng calloccó thể khiến callocbộ nhớ của nó trở thành bộ nhớ bằng 0 nếu không có HĐH hoặc đó không phải là HĐH đa người dùng ưa thích mà ngăn các trang để ngăn chặn rò rỉ thông tin giữa các quy trình.

Trên Linux nhúng, malloc có thể mmap(MAP_UNINITIALIZED|MAP_ANONYMOUS), chỉ được kích hoạt cho một số hạt nhân nhúng vì nó không an toàn trên hệ thống nhiều người dùng.


224
Các biến thể * alloc là khá dễ nhớ - phân bổ rõ ràng, cấp phát bộ nhớ, phân bổ lại.
Cascabel

43
Sử dụng malloc () nếu bạn định đặt mọi thứ bạn sử dụng trong không gian được phân bổ. Sử dụng calloc () nếu bạn sẽ để các phần của dữ liệu chưa được khởi tạo - và sẽ rất có ích nếu các phần chưa được đặt thành 0.
Jonathan Leffler

268
callockhông nhất thiết phải đắt hơn, vì HĐH có thể thực hiện một số thủ thuật để tăng tốc nó. Tôi biết rằng FreeBSD, khi nó nhận được bất kỳ thời gian CPU nhàn rỗi nào, sử dụng điều đó để chạy một quy trình đơn giản chỉ cần thực hiện và giải phóng các khối bộ nhớ đã xử lý và đánh dấu các khối do đó xử lý bằng cờ. Vì vậy, khi bạn làm calloc, trước tiên, nó sẽ cố gắng tìm một trong những khối không có trước như vậy và chỉ đưa nó cho bạn - và rất có thể nó sẽ tìm thấy một khối.
Pavel Minaev

28
Tôi có xu hướng cảm thấy rằng nếu mã của bạn trở nên "an toàn hơn" do kết quả của việc phân bổ không nhập theo mặc định, thì mã của bạn không đủ an toàn cho dù bạn sử dụng malloc hay calloc. Sử dụng malloc là một chỉ báo tốt cho thấy dữ liệu cần khởi tạo - Tôi chỉ sử dụng calloc trong trường hợp 0 ​​byte đó thực sự có ý nghĩa. Cũng lưu ý rằng calloc không nhất thiết phải làm những gì bạn nghĩ cho các loại không phải là char. Không ai thực sự sử dụng các biểu diễn bẫy nữa, hoặc các phao không phải của IEEE, nhưng đó không phải là lý do để nghĩ rằng mã của bạn thực sự di động khi không.
Steve Jessop

18
@SteveJessop "An toàn hơn" không phải là từ chính xác. Tôi nghĩ "Xác định" là thuật ngữ tốt hơn. Mã có tính quyết định nhiều hơn là có các lỗi phụ thuộc vào chuỗi thời gian và dữ liệu, sẽ dễ dàng hơn để cô lập các lỗi. Calloc đôi khi là một cách dễ dàng để có được tính quyết định đó, so với khởi tạo rõ ràng.
dennis

362

Một sự khác biệt ít được biết đến là trong các hệ điều hành có phân bổ bộ nhớ lạc quan, như Linux, con trỏ được trả về mallockhông được hỗ trợ bởi bộ nhớ thực cho đến khi chương trình thực sự chạm vào nó.

callocthực sự chạm vào bộ nhớ (nó ghi các số 0 trên nó) và do đó bạn sẽ chắc chắn HĐH đang sao lưu phân bổ với RAM thực tế (hoặc trao đổi). Đây cũng là lý do tại sao nó chậm hơn malloc (không chỉ phải bằng 0, HĐH cũng phải tìm một vùng nhớ phù hợp bằng cách có thể hoán đổi các tiến trình khác)

Xem ví dụ câu hỏi SO này để thảo luận thêm về hành vi của malloc


49
callockhông cần viết số không. Nếu khối được phân bổ bao gồm hầu hết các trang 0 mới được cung cấp bởi hệ điều hành, nó có thể khiến những trang này không bị ảnh hưởng. Điều này tất nhiên đòi hỏi callocphải được điều chỉnh cho hệ điều hành chứ không phải là một chức năng thư viện chung trên đầu trang malloc. Hoặc, một người triển khai có thể thực hiện callocso sánh từng từ với số 0 trước khi bỏ từ đó. Điều này sẽ không tiết kiệm thời gian, nhưng nó sẽ tránh làm bẩn các trang mới.
R .. GitHub DỪNG GIÚP ICE

3
@R .. lưu ý thú vị. Nhưng trong thực tế, việc thực hiện như vậy có tồn tại trong tự nhiên không?
Isak Savo

10
Tất cả các dlmalloctriển khai giống như bỏ qua memsetnếu phần được lấy thông qua mmaping các trang ẩn danh mới (hoặc tương đương). Thông thường loại phân bổ này được sử dụng cho các khối lớn hơn, bắt đầu từ 256k hoặc hơn. Tôi không biết về bất kỳ triển khai nào so sánh với số 0 trước khi viết số 0 ngoài bản thân tôi.
R .. GitHub DỪNG GIÚP ICE

1
omalloccũng bỏ qua memset; callockhông cần phải chạm vào bất kỳ trang nào chưa được ứng dụng (bộ đệm trang) sử dụng. Mặc dù, thực hiện cực kỳ nguyên thủycalloc khác nhau.
mirabilos

10
Calloc của glibc kiểm tra xem nó có nhận được bộ nhớ mới từ HĐH không. Nếu vậy, nó biết rằng nó KHÔNG cần phải viết nó, bởi vì mmap (..., MAP_ANONYMOUS) trả về bộ nhớ đã bị xóa.
Peter Cordes

112

Một lợi thế thường bị bỏ qua calloclà (việc triển khai tuân thủ) nó sẽ giúp bảo vệ bạn khỏi các lỗ hổng tràn số nguyên. Đối chiếu:

size_t count = get_int32(file);
struct foo *bar = malloc(count * sizeof *bar);

so với

size_t count = get_int32(file);
struct foo *bar = calloc(count, sizeof *bar);

Cái trước có thể dẫn đến một sự phân bổ nhỏ và bộ đệm tiếp theo tràn ra, nếu countlớn hơn SIZE_MAX/sizeof *bar. Cái sau sẽ tự động thất bại trong trường hợp này vì một đối tượng lớn không thể được tạo.

Tất nhiên, bạn có thể phải đề phòng các triển khai không tuân thủ mà chỉ cần bỏ qua khả năng tràn ... Nếu đây là mối lo ngại về các nền tảng bạn nhắm mục tiêu, dù sao bạn cũng sẽ phải thực hiện kiểm tra thủ công cho tràn.


17
Rõ ràng là tràn số học là nguyên nhân gây ra lỗ hổng OpenSSH vào năm 2002. Bài viết hay từ OpenBSD về những nguy hiểm của điều này với các chức năng liên quan đến bộ nhớ: undeadly.org/cgi?action=article&sid=20060330071917
Philip P.

4
@KomradeP.: Thú vị. Đáng buồn là bài viết bạn liên kết có thông tin sai ngay từ đầu. Ví dụ với charkhông một tràn mà là một chuyển đổi thực hiện xác định khi giao kết quả trở lại vào một charđối tượng.
R .. GitHub DỪNG GIÚP ICE

Có lẽ nó chỉ nhằm mục đích minh họa. Bởi vì trình biên dịch có khả năng tối ưu hóa đi dù sao đi nữa. Của tôi biên dịch vào asm này: đẩy 1.
Philip P.

1
@tristopia: Vấn đề không phải là mã có thể khai thác trên tất cả các triển khai, nhưng nó không chính xác nếu không có các giả định bổ sung và do đó không sử dụng đúng / di động.
R .. GitHub DỪNG GIÚP ICE

3
@tristopia: Nếu chế độ suy nghĩ của bạn là " size_t64-bit thì không có vấn đề gì", đó là một cách suy nghĩ thiếu sót sẽ dẫn đến lỗi bảo mật. size_tlà một loại trừu tượng đại diện cho các kích thước và không có lý do gì để nghĩ rằng sản phẩm tùy ý của số 32 bit và size_t(lưu ý: sizeof *barvề nguyên tắc có thể lớn hơn 2 ^ 32 khi triển khai C 64 bit!) phù hợp size_t.
R .. GitHub DỪNG GIÚP ICE

37

Tài liệu này làm cho calloc trông giống như malloc, không khởi tạo bộ nhớ; Đây không phải là sự khác biệt chính! Ý tưởng của calloc là bỏ qua ngữ nghĩa copy-on-write để cấp phát bộ nhớ. Khi bạn phân bổ bộ nhớ với calloc, tất cả các ánh xạ tới cùng một trang vật lý được khởi tạo thành không. Khi bất kỳ trang nào của bộ nhớ được phân bổ được ghi vào một trang vật lý được phân bổ. Điều này thường được sử dụng để tạo các bảng băm HUGE, ví dụ vì các phần của hàm băm trống không được hỗ trợ bởi bất kỳ bộ nhớ bổ sung (trang) nào; họ vui vẻ chỉ vào trang không khởi tạo, có thể được chia sẻ giữa các quy trình.

Bất kỳ ghi vào địa chỉ ảo nào cũng được ánh xạ tới một trang, nếu trang đó là trang 0, một trang vật lý khác được phân bổ, trang 0 được sao chép ở đó và luồng điều khiển được trả về quy trình máy khách. Điều này hoạt động tương tự như cách các tập tin ánh xạ bộ nhớ, bộ nhớ ảo, vv hoạt động .. nó sử dụng phân trang.

Đây là một câu chuyện tối ưu hóa về chủ đề: http://bloss.fau.de/hager/2007/05/08/benchmarking-fun-with-calloc-and-zero-pages/


26

Không có sự khác biệt về kích thước của khối bộ nhớ được phân bổ. callocchỉ lấp đầy khối bộ nhớ với mẫu tất cả các bit vật lý. Trong thực tế, người ta thường cho rằng các đối tượng nằm trong khối bộ nhớ được phân bổ calloccó giá trị ban đầu như thể chúng được khởi tạo bằng chữ 0, nghĩa là các số nguyên phải có giá trị 0, biến dấu phẩy động - giá trị của 0.0con trỏ - giá trị con trỏ null thích hợp , và như thế.

Từ quan điểm phạm vi, mặc dù, calloc(cũng như memset(..., 0, ...)) chỉ được đảm bảo để khởi tạo đúng (với số không) các đối tượng loại unsigned char. Mọi thứ khác không được đảm bảo để được khởi tạo đúng cách và có thể chứa cái gọi là biểu diễn bẫy , gây ra hành vi không xác định. Nói cách khác, đối với bất kỳ loại nào khác ngoài unsigned charpatterm all-zero-bit đã nói ở trên có thể đại diện cho một giá trị bất hợp pháp, đại diện bẫy.

Sau đó, trong một trong các Chương trình kỹ thuật theo tiêu chuẩn C99, hành vi được xác định cho tất cả các loại số nguyên (có ý nghĩa). Tức là chính thức, trong ngôn ngữ C hiện tại, bạn chỉ có thể khởi tạo các kiểu số nguyên với calloc(và memset(..., 0, ...)). Sử dụng nó để khởi tạo bất cứ điều gì khác trong trường hợp chung dẫn đến hành vi không xác định, từ quan điểm của ngôn ngữ C.

Trong thực tế, callochoạt động, như chúng ta đều biết :), nhưng bạn có muốn sử dụng nó hay không (xem xét ở trên) là tùy thuộc vào bạn. Cá nhân tôi thích tránh nó hoàn toàn, mallocthay vào đó sử dụng và thực hiện khởi tạo của riêng tôi.

Cuối cùng, một chi tiết quan trọng khác là callocđược yêu cầu để tính kích thước khối cuối cùng bên trong , bằng cách nhân kích thước phần tử với số phần tử. Trong khi làm điều đó, callocphải xem cho tràn số học có thể. Nó sẽ dẫn đến việc phân bổ không thành công (con trỏ null) nếu kích thước khối được yêu cầu không thể được tính toán chính xác. Trong khi đó, mallocphiên bản của bạn không cố gắng xem tràn. Nó sẽ phân bổ một số lượng bộ nhớ "không thể đoán trước" trong trường hợp tràn xảy ra.


Theo đoạn "chi tiết quan trọng khác": điều đó dường như gây ra memset(p, v, n * sizeof type);vấn đề vì n * sizeof typecó thể tràn. Đoán tôi sẽ cần phải sử dụng một for(i=0;i<n;i++) p[i]=v;vòng lặp cho mã mạnh mẽ.
chux - Tái lập lại

Sẽ rất hữu ích nếu có một phương tiện tiêu chuẩn mà mã có thể khẳng định rằng việc triển khai phải sử dụng all-bits-zero làm con trỏ null (từ chối biên dịch theo cách khác), vì có tồn tại các triển khai sử dụng các biểu diễn con trỏ null khác, nhưng chúng là tương đối hiếm; mã không phải chạy trên các triển khai như vậy có thể nhanh hơn nếu nó có thể sử dụng calloc () hoặc bộ nhớ để khởi tạo các mảng của con trỏ.
supercat

@chux Không, nếu một mảng có ncác phần tử tồn tại trong đó một phần tử có kích thước sizeof type, thì n*sizeof typekhông thể tràn, bởi vì kích thước tối đa của bất kỳ đối tượng nào phải nhỏ hơn SIZE_MAX.
12431234123412341234123

@ 12431234123412341234123 Đúng về kích thước mảng <= SIZE_MAX, nhưng không có mảng nào ở đây. Con trỏ được trả về từ calloc()có thể trỏ đến bộ nhớ được phân bổ vượt quá SIZE_MAX. Nhiều triển khai thực hiện giới hạn sản phẩm của 2 calloc()đối số SIZE_MAX, nhưng thông số C không áp đặt giới hạn đó.
chux - Phục hồi Monica

21

từ một bài viết Điểm chuẩn thú vị với calloc () và không có trang nào trên Blog của Georg Hager

Khi phân bổ bộ nhớ bằng calloc (), lượng bộ nhớ được yêu cầu không được phân bổ ngay. Thay vào đó, tất cả các trang thuộc về khối bộ nhớ được kết nối với một trang có chứa tất cả các số 0 bằng một số phép thuật MMU (liên kết bên dưới). Nếu các trang đó chỉ được đọc (điều này đúng với các mảng b, c và d trong phiên bản gốc của điểm chuẩn), thì dữ liệu được cung cấp từ trang 0 duy nhất, dĩ nhiên - phù hợp với bộ đệm. Quá nhiều cho các hạt nhân vòng lặp bộ nhớ. Nếu một trang được ghi vào (bất kể như thế nào), sẽ xảy ra lỗi, trang Real real được ánh xạ và trang 0 được sao chép vào bộ nhớ. Đây được gọi là copy-on-write, một phương pháp tối ưu hóa nổi tiếng (mà tôi thậm chí đã dạy nhiều lần trong các bài giảng C ++ của mình). Sau đó,


liên kết đâu?
Rupesh Yadav.

2
dòng đầu tiên của câu trả lời chứa liên kết đến Blog của Georg Hager.
Ashish Chavan

11

callocthường là malloc+memset0

Nói chung là tốt hơn một chút để sử dụng malloc+memsetrõ ràng, đặc biệt là khi bạn đang làm một cái gì đó như:

ptr=malloc(sizeof(Item));
memset(ptr, 0, sizeof(Item));

Điều đó tốt hơn bởi vì sizeof(Item)biết trình biên dịch tại thời điểm biên dịch và trong hầu hết các trường hợp, trình biên dịch sẽ thay thế nó bằng các hướng dẫn tốt nhất có thể để bộ nhớ không. Mặt khác, nếu memsetđang xảy ra calloc, kích thước tham số của phân bổ không được biên dịch trong callocmã và thực memsetthường được gọi, thường sẽ chứa mã để thực hiện điền theo từng byte cho đến khi biên dài, hơn chu kỳ để điền tăng bộ nhớ trong sizeof(long)các khối và cuối cùng là byte theo byte của không gian còn lại. Ngay cả khi người cấp phát đủ thông minh để gọi một số aligned_memsetthì nó vẫn sẽ là một vòng lặp chung.

Một ngoại lệ đáng chú ý là khi bạn đang thực hiện malloc / calloc của một khối bộ nhớ rất lớn (một số kilobyte power_of_two) trong đó phân bổ trường hợp có thể được thực hiện trực tiếp từ kernel. Vì các nhân hệ điều hành thường sẽ loại bỏ tất cả bộ nhớ mà chúng cung cấp vì lý do bảo mật, nên calloc đủ thông minh có thể trả lại nó với số 0 bổ sung. Một lần nữa - nếu bạn chỉ phân bổ một cái gì đó bạn biết là nhỏ, bạn có thể tốt hơn với malloc + memset hiệu năng-khôn ngoan.


+1 cho lời nhắc rằng việc triển khai chung chức năng trong thư viện hệ thống không nhất thiết phải nhanh hơn thao tác tương tự trong mã người dùng.
Patrick Schlüter

1
Ngoài ra còn có một điểm thứ hai làm cho calloc()chậm hơn malloc(): phép nhân cho kích thước. calloc()được yêu cầu sử dụng phép nhân chung (nếu size_tlà 64 bit, ngay cả hoạt động 64 bit rất tốn kém * 64 bit = 64 bit) trong khi malloc () thường sẽ có hằng số thời gian biên dịch.
Patrick Schlüter

4
glocc calloc có một số thông minh để quyết định làm thế nào để xóa phần được trả lại một cách hiệu quả nhất, ví dụ, đôi khi chỉ một phần của nó cần xóa, và cũng là một phần xóa không được kiểm soát lên đến 9 * sizeof (size_t). Bộ nhớ là bộ nhớ, xóa 3 byte mỗi lần sẽ không nhanh hơn chỉ vì sau đó bạn sẽ sử dụng nó để giữ struct foo { char a,b,c; };. callocluôn luôn tốt hơn malloc+ memset, nếu bạn luôn xóa toàn bộ mallocvùng ed. calloccũng có một kiểm tra cẩn thận nhưng hiệu quả đối với các phần tử kích thước int *.
Peter Cordes

8

Sự khác biệt 1:

malloc() thường phân bổ khối bộ nhớ và nó là phân đoạn bộ nhớ khởi tạo.

calloc() cấp phát khối bộ nhớ và khởi tạo tất cả khối bộ nhớ thành 0.

Sự khác biệt 2:

Nếu bạn xem xét malloc()cú pháp, nó sẽ chỉ mất 1 đối số. Hãy xem xét ví dụ sau đây:

data_type ptr = (cast_type *)malloc( sizeof(data_type)*no_of_blocks );

Ví dụ: Nếu bạn muốn phân bổ 10 khối bộ nhớ cho kiểu int,

int *ptr = (int *) malloc(sizeof(int) * 10 );

Nếu bạn xem xét calloc()cú pháp, nó sẽ mất 2 đối số. Hãy xem xét ví dụ sau đây:

data_type ptr = (cast_type *)calloc(no_of_blocks, (sizeof(data_type)));

Ví dụ: nếu bạn muốn phân bổ 10 khối bộ nhớ cho kiểu int và Khởi tạo tất cả số đó thành ZERO,

int *ptr = (int *) calloc(10, (sizeof(int)));

Tương tự:

Cả hai malloc()calloc()sẽ trả về void * theo mặc định nếu chúng không được chọn.!


Và tại sao bạn giữ data_type và cast_type khác nhau?
Bán hết

7

Có hai sự khác biệt.
Đầu tiên, là về số lượng đối số. malloc()nhận một đối số (bộ nhớ được yêu cầu theo byte), trong khi calloc()cần hai đối số.
Thứ hai, malloc()không khởi tạo bộ nhớ được phân bổ, trong khi calloc()khởi tạo bộ nhớ được phân bổ thành ZERO.

  • calloc()Phân bổ một vùng nhớ, chiều dài sẽ là sản phẩm của các tham số của nó. calloclấp đầy bộ nhớ với ZERO và trả về một con trỏ tới byte đầu tiên. Nếu nó không xác định đủ không gian, nó sẽ trả về một NULLcon trỏ.

Cú pháp: ptr_var=(cast_type *)calloc(no_of_blocks , size_of_each_block); tức làptr_var=(type *)calloc(n,s);

  • malloc()cấp phát một khối bộ nhớ duy nhất của REQUSTED SIZE và trả về một con trỏ tới byte đầu tiên. Nếu không xác định được lượng bộ nhớ được yêu cầu, nó sẽ trả về một con trỏ rỗng.

Cú pháp: ptr_var=(cast_type *)malloc(Size_in_bytes); Các malloc()chức năng chụp một đối số, đó là số byte để phân bổ, trong khi calloc()chức năng nhận hai đối số, một là số phần tử, và người kia là số byte để phân bổ cho mỗi người trong số những yếu tố này. Ngoài ra, calloc()khởi tạo không gian được phân bổ cho số không, trong khi malloc()không.


6

Các calloc()chức năng được khai báo trong <stdlib.h>phần đầu cung cấp một vài lợi thế hơn các malloc()chức năng.

  1. Nó phân bổ bộ nhớ dưới dạng một số phần tử có kích thước nhất định và
  2. Nó khởi tạo bộ nhớ được phân bổ sao cho tất cả các bit bằng không.

6

malloc()calloc()là các hàm từ thư viện chuẩn C cho phép cấp phát bộ nhớ động, nghĩa là cả hai đều cho phép cấp phát bộ nhớ trong thời gian chạy.

Nguyên mẫu của chúng như sau:

void *malloc( size_t n);
void *calloc( size_t n, size_t t)

Có hai sự khác biệt chủ yếu giữa hai:

  • Hành vi: malloc()phân bổ một khối bộ nhớ, mà không khởi tạo nó và đọc nội dung từ khối này sẽ dẫn đến các giá trị rác. calloc()mặt khác, phân bổ một khối bộ nhớ và khởi tạo nó thành số không, và rõ ràng việc đọc nội dung của khối này sẽ dẫn đến số không.

  • Cú pháp: malloc()lấy 1 đối số (kích thước được phân bổ) và calloc()lấy hai đối số (số lượng khối được phân bổ và kích thước của mỗi khối).

Giá trị trả về từ cả hai là một con trỏ tới khối bộ nhớ được phân bổ, nếu thành công. Nếu không, NULL sẽ được trả về cho biết lỗi cấp phát bộ nhớ.

Thí dụ:

int *arr;

// allocate memory for 10 integers with garbage values
arr = (int *)malloc(10 * sizeof(int)); 

// allocate memory for 10 integers and sets all of them to 0
arr = (int *)calloc(10, sizeof(int));

Các chức năng tương tự như calloc()có thể đạt được bằng cách sử dụng malloc()memset():

// allocate memory for 10 integers with garbage values   
arr= (int *)malloc(10 * sizeof(int));
// set all of them to 0
memset(arr, 0, 10 * sizeof(int)); 

Lưu ý rằng malloc()tốt nhất là được sử dụng hơn calloc()vì nó nhanh hơn. Nếu không khởi tạo các giá trị là muốn, calloc()thay vào đó hãy sử dụng .


5

Một sự khác biệt chưa được đề cập: giới hạn kích thước

void *malloc(size_t size)Chỉ có thể phân bổ tối đa SIZE_MAX.

void *calloc(size_t nmemb, size_t size);Có thể phân bổ lên SIZE_MAX*SIZE_MAX.

Khả năng này thường không được sử dụng trong nhiều nền tảng với địa chỉ tuyến tính. Hệ thống như vậy giới hạn calloc()với nmemb * size <= SIZE_MAX.

Hãy xem xét một loại 512 byte được gọi disk_sectorvà mã muốn sử dụng nhiều cung . Ở đây, mã chỉ có thể sử dụng tối đa các SIZE_MAX/sizeof disk_sectorngành.

size_t count = SIZE_MAX/sizeof disk_sector;
disk_sector *p = malloc(count * sizeof *p);

Hãy xem xét những điều sau đây cho phép phân bổ thậm chí còn lớn hơn.

size_t count = something_in_the_range(SIZE_MAX/sizeof disk_sector + 1, SIZE_MAX)
disk_sector *p = calloc(count, sizeof *p);

Bây giờ nếu một hệ thống như vậy có thể cung cấp một phân bổ lớn như vậy là một vấn đề khác. Hầu hết ngày nay sẽ không. Tuy nhiên, nó đã xảy ra trong nhiều năm khi SIZE_MAXlà 65535. Theo luật của Moore , nghi ngờ điều này sẽ xảy ra vào khoảng năm 2030 với một số mô hình bộ nhớ nhất định SIZE_MAX == 4294967295và nhóm bộ nhớ trong 100 GB GB.


2
Nói chung, size_t sẽ có khả năng giữ kích thước của loại đối tượng lớn nhất mà chương trình có thể xử lý. Một hệ thống có size_t là 32 bit dường như không thể xử lý phân bổ lớn hơn 4294967295 byte và một hệ thống có thể xử lý phân bổ có kích thước gần như chắc chắn sẽ size_tlớn hơn 32 bit. Câu hỏi duy nhất là liệu sử dụng callocvới các giá trị có sản phẩm vượt quá SIZE_MAXcó thể được dựa vào để mang lại số 0 hay không thay vì trả về một con trỏ cho phân bổ nhỏ hơn.
supercat

Đồng ý về khái quát của bạn , nhưng thông số C cho phép calloc()phân bổ vượt quá SIZE_MAX. Nó đã xảy ra trong quá khứ với 16 bit size_tvà khi bộ nhớ tiếp tục giảm giá, tôi thấy không có lý do gì nó sẽ không thể xảy ra trong tương lai ngay cả khi nó không phổ biến .
chux - Phục hồi lại

1
Tiêu chuẩn C giúp mã có thể yêu cầu phân bổ có kích thước vượt quá SIZE_MAX. Nó chắc chắn không yêu cầu rằng có bất kỳ tình huống nào theo đó phân bổ như vậy có thể thành công; Tôi không chắc chắn có bất kỳ lợi ích cụ thể nào từ việc bắt buộc các triển khai không thể xử lý các phân bổ đó phải trả lại NULL(đặc biệt là một số triển khai có malloccon trỏ trả về không gian chưa được cam kết và có thể không khả dụng khi mã thực sự sử dụng nó).
supercat

Hơn nữa, nơi trước đây có thể có các hệ thống có phạm vi địa chỉ khả dụng vượt quá số nguyên có thể biểu thị lớn nhất, tôi không thấy bất kỳ khả năng thực tế nào xảy ra nữa, vì điều đó đòi hỏi dung lượng lưu trữ hàng tỷ gigabyte. Ngay cả khi Định luật Moore tiếp tục giữ vững, đi từ điểm 32 bit không còn đủ đến mức 64 bit ngừng là đủ sẽ mất gấp đôi thời gian từ khi 16 bit đủ đến điểm 32 bit 't.
supercat

1
Tại sao một thực hiện mà có thể chứa một phân bổ đơn vượt quá 4G không xác định size_tđến uint64_t?
supercat

2

Số khối:
malloc () gán một khối bộ nhớ được yêu cầu,
calloc () gán nhiều khối của bộ nhớ được yêu cầu

Khởi tạo:
malloc () - không xóa và khởi tạo bộ nhớ được phân bổ.
calloc () - khởi tạo bộ nhớ được cấp phát bằng 0.

Tốc độ:
malloc () là nhanh.
calloc () chậm hơn malloc ().

Đối số & Cú pháp:
malloc () nhận 1 đối số:

  1. byte

    • Số lượng byte được phân bổ

calloc () nhận 2 đối số:

  1. chiều dài

    • số lượng bộ nhớ được phân bổ
  2. byte
    • số lượng byte được phân bổ ở mỗi khối bộ nhớ
void *malloc(size_t bytes);         
void *calloc(size_t length, size_t bytes);      

Cách thức phân bổ bộ nhớ:
Hàm malloc gán bộ nhớ của 'kích thước' mong muốn từ heap có sẵn.
Hàm calloc gán bộ nhớ có kích thước bằng 'num * size'.

Ý nghĩa về tên:
Tên malloc có nghĩa là "cấp phát bộ nhớ".
Tên calloc có nghĩa là "phân bổ liền kề".

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.