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?
ptr = calloc(MAXELEMS, sizeof(*ptr));
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?
ptr = calloc(MAXELEMS, sizeof(*ptr));
Câu trả lời:
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 calloc
triể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 malloc
nhận được nhiều trang hơn từ HĐH; calloc
chỉ tận dụng sự bảo đảm của HĐH.
Điều này có nghĩa là calloc
bộ 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 calloc
có thể khiến calloc
bộ 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.
calloc
khô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.
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ề malloc
khô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ó.
calloc
thự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
calloc
khô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 calloc
phả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 calloc
so 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.
dlmalloc
triển khai giống như bỏ qua memset
nếu phần được lấy thông qua mmap
ing 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.
omalloc
cũng bỏ qua memset
; calloc
khô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.
Một lợi thế thường bị bỏ qua calloc
là (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 count
lớ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.
char
là khô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.
size_t
64-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_t
là 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 *bar
về nguyên tắc có thể lớn hơn 2 ^ 32 khi triển khai C 64 bit!) phù hợp size_t
.
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/
Không có sự khác biệt về kích thước của khối bộ nhớ được phân bổ. calloc
chỉ 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ổ calloc
có 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.0
con 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 char
patterm 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ế, calloc
hoạ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, malloc
thay 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 đó, calloc
phả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 đó, malloc
phiê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.
memset(p, v, n * sizeof type);
vấn đề vì n * sizeof type
có 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ẽ.
n
các phần tử tồn tại trong đó một phần tử có kích thước sizeof type
, thì n*sizeof type
khô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
.
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 đó.
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 đó,
calloc
thường là malloc+memset
0
Nói chung là tốt hơn một chút để sử dụng malloc+memset
rõ 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 calloc
mã và thực memset
thườ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_memset
thì 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.
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_t
là 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.
struct foo { char a,b,c; };
. calloc
luôn luôn tốt hơn malloc
+ memset
, nếu bạn luôn xóa toàn bộ malloc
vùng ed. calloc
cũ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 *.
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()
và calloc()
sẽ trả về void * theo mặc định nếu chúng không được chọn.!
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ó. calloc
lấ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 NULL
con 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.
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.
malloc()
và 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()
và 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 .
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_sector
và 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_sector
ngà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_MAX
là 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 == 4294967295
và nhóm bộ nhớ trong 100 GB GB.
size_t
lớn hơn 32 bit. Câu hỏi duy nhất là liệu sử dụng calloc
với các giá trị có sản phẩm vượt quá SIZE_MAX
có 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.
calloc()
phân bổ vượt quá SIZE_MAX
. Nó đã xảy ra trong quá khứ với 16 bit size_t
và 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 .
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ó malloc
con 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ó).
size_t
đến uint64_t
?
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ố:
byte
calloc () nhận 2 đối số:
chiều dài
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ề".
malloc
gia đình