Różnica między malloc a calloc?

779

Jaka jest różnica między robieniem:

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

lub:

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

Kiedy warto używać calloc zamiast malloc lub odwrotnie?

użytkownik105033
źródło
32
W C niemalloc
rzucasz
8
W C możesz napisać powyższe bardziej ogólnie jako:ptr = calloc(MAXELEMS, sizeof(*ptr));
chqrlie
7
Ciekawy post na temat różnicy między calloc a malloc + memset vorpus.org/blog/why-does-calloc-exist
ddddavidee
2
@ddddavidee Ja też znalazłem tego bloga po tym, jak byłem niezadowolony z tylu odpowiedzi w sieci. Nathaniel J. Smith zasługuje na ponad 100 punktów SO za swoją analizę.
lifebalance

Odpowiedzi:

849

calloc()daje bufor inicjowany zerem, malloc()pozostawiając pamięć niezainicjowaną.

W przypadku dużych alokacji większość callocimplementacji w głównych systemach operacyjnych otrzyma strony o znanym wyzerowaniu z systemu operacyjnego (np. Przez POSIX mmap(MAP_ANONYMOUS)lub Windows VirtualAlloc), więc nie trzeba ich zapisywać w przestrzeni użytkownika. W ten sposób normalny mallocpobiera także więcej stron z systemu operacyjnego; callockorzysta po prostu z gwarancji systemu operacyjnego.

Oznacza to, że callocpamięć może być nadal „czysta” i leniwie przydzielona, ​​a także kopiowana podczas zapisu mapowana na ogólnosystemową wspólną fizyczną stronę zer. (Zakładając, że system ma pamięć wirtualną.)

Niektóre kompilatory mogą nawet zoptymalizować malloc + memset (0) w calloc, ale powinieneś jawnie używać calloc, jeśli chcesz, aby pamięć była odczytywana jako 0.

Jeśli nie zamierzasz nigdy czytać pamięci przed jej zapisaniem, użyj jej, mallocaby (potencjalnie) dać brudną pamięć z wewnętrznej wolnej listy zamiast pobierania nowych stron z systemu operacyjnego. (Lub zamiast zerowania bloku pamięci na wolnej liście dla małego przydziału).


Wbudowane implementacje callocmogą pozostawić to calloczeru pamięci, jeśli nie ma systemu operacyjnego, lub nie jest to fantazyjny system operacyjny dla wielu użytkowników, który zeruje strony, aby zatrzymać wycieki informacji między procesami.

W przypadku wbudowanego Linuxa malloc mógłby mmap(MAP_UNINITIALIZED|MAP_ANONYMOUS), który jest włączony tylko dla niektórych wbudowanych jąder, ponieważ nie jest bezpieczny w systemie dla wielu użytkowników.

Fred Larson
źródło
224
* Warianty alokacji są dość mnemoniczne - wyczyść, przydziel pamięć, ponownie przydziel.
Cascabel,
43
Użyj malloc (), jeśli zamierzasz ustawić wszystko, czego używasz w przydzielonym miejscu. Użyj calloc (), jeśli chcesz pozostawić części danych niezainicjowane - i korzystne byłoby zerowanie niezbadanych części.
Jonathan Leffler,
268
callocniekoniecznie jest droższy, ponieważ system operacyjny może wykonać pewne sztuczki, aby go przyspieszyć. Wiem, że FreeBSD, kiedy robi się bezczynny procesor, używa go do uruchomienia prostego procesu, który po prostu się kręci i zeruje zwolnione bloki pamięci, i zaznacza w ten sposób bloki flagą. Więc kiedy to zrobisz calloc, najpierw próbuje znaleźć jeden z takich zerowanych bloków i po prostu dać ci go - i najprawdopodobniej go znajdzie.
Pavel Minaev
28
Wydaje mi się, że jeśli twój kod stanie się „bezpieczniejszy” w wyniku domyślnie alokacji zerowej, wówczas kod nie będzie wystarczająco bezpieczny, niezależnie od tego, czy używasz malloc czy calloc. Korzystanie z malloc jest dobrym wskaźnikiem, że dane wymagają inicjalizacji - używam calloc tylko w przypadkach, w których te 0 bajtów są rzeczywiście znaczące. Zauważ też, że calloc niekoniecznie robi to, co myślisz dla typów innych niż char. Nikt tak naprawdę już nie używa reprezentacji pułapek, ani pływaków spoza IEEE, ale to nie usprawiedliwia myślenia, że ​​twój kod jest naprawdę przenośny, gdy tak nie jest.
Steve Jessop
18
@SteveJessop „Bezpieczniejsze” nie jest poprawnym słowem. Myślę, że „deterministyczny” jest lepszym określeniem. Kod, który jest bardziej deterministyczny niż zawiera awarie zależne od czasu i sekwencji danych, łatwiej będzie izolować awarie. Calloc jest czasem łatwym sposobem na uzyskanie tego determinizmu w porównaniu do jawnej inicjalizacji.
dennis
362

Mniej znaną różnicą jest to, że w systemach operacyjnych z optymistycznym przydziałem pamięci, takich jak Linux, zwracany wskaźnik mallocnie jest wspierany przez rzeczywistą pamięć, dopóki program jej nie dotknie.

callocrzeczywiście dotyka pamięci (zapisuje na niej zera), dzięki czemu będziesz mieć pewność, że system operacyjny popiera alokację rzeczywistą pamięcią RAM (lub zamianą). Dlatego też jest wolniejszy niż Malloc (nie tylko musi go wyzerować, ale system operacyjny musi również znaleźć odpowiedni obszar pamięci, ewentualnie zamieniając inne procesy)

Zobacz na przykład to SO pytanie do dalszej dyskusji na temat zachowania Malloc

Isak Savo
źródło
49
callocnie muszę pisać zer. Jeśli przydzielony blok składa się głównie z nowych zerowych stron dostarczonych przez system operacyjny, może pozostawić te nietknięte. To oczywiście wymaga callocdostrojenia do systemu operacyjnego, a nie ogólnej funkcji biblioteki malloc. Lub implementator może callocporównać każde słowo z zerem przed jego wyzerowaniem. Nie zaoszczędziłoby to czasu, ale uniknęłoby zabrudzenia nowych stron.
R .. GitHub ZATRZYMAJ LOD
3
@R .. interesująca uwaga. Ale w praktyce, czy takie wdrożenia istnieją na wolności?
Isak Savo
10
Wszystkie dlmallocimplementacje pomijają, memsetjeśli fragment został uzyskany poprzez wprowadzenie mmapnowych anonimowych stron (lub równoważnych). Zazwyczaj ten rodzaj alokacji jest stosowany dla większych porcji, zaczynając od około 256 tys. Nie znam żadnych implementacji, które dokonałyby porównania z zerem przed zapisaniem zera poza własnym.
R .. GitHub ZATRZYMAJ LÓD
1
omallocrównież pomija memset; callocnigdy nie musi dotykać stron, które nie są jeszcze używane przez aplikację (pamięć podręczna stron). Jednak niezwykle prymitywne callocimplementacje różnią się.
mirabilos
10
Calloc glibc sprawdza, czy pobiera świeżą pamięć z systemu operacyjnego. Jeśli tak, to wie, że NIE musi go pisać, ponieważ mmap (..., MAP_ANONYMOUS) zwraca pamięć, która jest już wyzerowana.
Peter Cordes,
111

Jedną z często pomijanych zalet callocjest to, że (zgodne implementacje) pomoże chronić cię przed podatnością na przepełnienie liczb całkowitych. Porównać:

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

vs.

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

To pierwsze może spowodować niewielką alokację i kolejne przepełnienia bufora, jeśli countjest większa niż SIZE_MAX/sizeof *bar. Ten ostatni automatycznie zawiedzie w tym przypadku, ponieważ nie można utworzyć tak dużego obiektu.

Oczywiście może być konieczne poszukiwanie implementacji niezgodnych, które po prostu ignorują możliwość przepełnienia ... Jeśli jest to problem na platformach, na które celujesz, będziesz musiał przeprowadzić ręczny test przepełnienia.

R .. GitHub ZATRZYMAJ LÓD
źródło
17
Najwyraźniej przepełnienie arytmetyczne spowodowało dziurę w OpenSSH w 2002 r. Dobry artykuł z OpenBSD na temat niebezpieczeństw związanych z pamięcią: undeadly.org/cgi?action=article&sid=20060330071917
Philip P.
4
@KomradeP .: Ciekawe. Niestety artykuł, który podlinkowałeś, zawiera na początku błędne informacje. W przykładzie z charjest nie przepełnienie lecz Konwersja realizacja zdefiniowane przy przypisywaniu tyłu wynik w charobiekcie.
R .. GitHub ZATRZYMAJ LÓD
Jest to prawdopodobnie wyłącznie w celach ilustracyjnych. Ponieważ kompilator i tak prawdopodobnie to zoptymalizuje. Mój kompiluje się w tym asm: push 1.
Philip P.
1
@tristopia: Nie chodzi o to, że kod nadaje się do wykorzystania we wszystkich implementacjach, ale że jest niepoprawny bez dodatkowych założeń, a zatem nie jest prawidłowy / przenośny.
R .. GitHub ZATRZYMAJ LÓD
3
@tristopia: Jeśli twój sposób myślenia jest „ size_t64-bitowy, więc nie ma problemu”, jest to błędny sposób myślenia, który doprowadzi do błędów bezpieczeństwa. size_tjest typem abstrakcyjnym, który reprezentuje rozmiary, i nie ma powodu, aby sądzić, że arbitralny iloczyn liczby 32-bitowej i size_t(uwaga: sizeof *barmoże w zasadzie być większy niż 2 ^ 32 w 64-bitowej implementacji C!) pasuje size_t.
R .. GitHub ZATRZYMAJ LÓD
37

Dokumentacja sprawia, że ​​calloc wygląda jak malloc, który po prostu zeruje pamięć; to nie jest podstawowa różnica! Ideą calloc jest powstrzymanie semantyki kopiowania przy zapisie w celu alokacji pamięci. Kiedy przydzielasz pamięć za pomocą calloc, wszystkie mapy są odwzorowywane na tę samą fizyczną stronę, która jest inicjowana na zero. Gdy którakolwiek ze stron przydzielonej pamięci zostanie zapisana na stronie fizycznej, zostaje przydzielona. Jest to często używane do tworzenia OGROMNYCH tabel skrótów, na przykład ponieważ puste części nie są zabezpieczone żadną dodatkową pamięcią (stronami); szczęśliwie wskazują pojedynczą stronę zainicjowaną przez zero, którą można nawet udostępniać między procesami.

Każdy zapis na adres wirtualny jest odwzorowywany na stronę, jeśli ta strona jest stroną zerową, przydzielana jest inna strona fizyczna, strona zerowa jest tam kopiowana, a przepływ sterowania jest zwracany do procesu klienta. Działa to w taki sam sposób, jak działają pliki mapowane w pamięci, pamięć wirtualna itp., Używa stronicowania.

Oto jedna historia optymalizacji na ten temat: http://blogs.fau.de/hager/2007/05/08/benchmarking-fun-with-calloc-and-zero-pages/

t0rakka
źródło
26

Nie ma różnicy w wielkości przydzielonego bloku pamięci. callocpo prostu wypełnia blok pamięci fizycznym wzorem z zerowymi bitami. W praktyce często przyjmuje się, że obiekty znajdujące się w bloku pamięci przydzielonym z callocmają wartość początkową, tak jakby były inicjowane dosłownie 0, tzn. Liczby całkowite powinny mieć wartość 0zmiennych zmiennoprzecinkowych - wartość 0.0wskaźników, odpowiednią wartość wskaźnika zerowego , i tak dalej.

Jednak z pedantycznego punktu widzenia calloc(jak również memset(..., 0, ...)) zagwarantowana jest tylko właściwa inicjalizacja (zerami) obiektów typu unsigned char. Nie można zagwarantować, że wszystko inne zostanie poprawnie zainicjowane i może zawierać tak zwaną reprezentację pułapki , która powoduje niezdefiniowane zachowanie. Innymi słowy, dla dowolnego typu innego niż unsigned charwspomniany wzorzec z zerowymi bitami może reprezentować niedozwoloną wartość, reprezentację pułapki.

Później, w jednej ze sprostowań technicznych do standardu C99, zdefiniowano zachowanie dla wszystkich typów liczb całkowitych (co ma sens). To znaczy formalnie, w bieżącym języku C możesz inicjować tylko typy całkowite za pomocą calloc(i memset(..., 0, ...)). Używanie go do inicjowania czegokolwiek innego w ogólnym przypadku prowadzi do nieokreślonego zachowania z punktu widzenia języka C.

W praktyce callocdziała, jak wszyscy wiemy :), ale to, czy chcesz go użyć (biorąc pod uwagę powyższe), zależy od Ciebie. Osobiście wolę całkowicie tego uniknąć, malloczamiast tego użyć i wykonać własną inicjalizację.

W końcu, kolejnym ważnym szczegółem jest to callocjest wymagane do obliczenia ostatecznego rozmiaru bloku wewnętrznie przez pomnożenie wielkości elementu liczby elementów. Robiąc to, callocnależy uważać na ewentualne przepełnienie arytmetyczne. Spowoduje to nieudaną alokację (wskaźnik zerowy), jeśli nie można poprawnie obliczyć żądanego rozmiaru bloku. Tymczasem twoja mallocwersja nie próbuje wykryć przepełnienia. Przydzieli pewną „nieprzewidywalną” ilość pamięci na wypadek przepełnienia.

Mrówka
źródło
Zgodnie z akapitem „inny ważny szczegół”: wydaje się to memset(p, v, n * sizeof type);stanowić problem, ponieważ n * sizeof typemoże się przepełnić. Chyba będę musiał użyć for(i=0;i<n;i++) p[i]=v;pętli dla solidnego kodu.
chux - Przywróć Monikę
Byłoby pomocne, gdyby istniały standardowe środki, za pomocą których kod mógłby twierdzić, że implementacja musi używać all-bit-zero jako wskaźnika zerowego (odmawiając kompilacji w przeciwnym razie), ponieważ istnieją implementacje, które używają innych reprezentacji zerowego wskaźnika, ale są one stosunkowo rzadki; kod, który nie musi działać na takich implementacjach, może być szybszy, jeśli może użyć calloc () lub memset do zainicjowania tablic wskaźników.
supercat
@chux Nie, jeśli istnieje tablica z nelementami, w której element ma rozmiar sizeof type, to n*sizeof typenie może się przepełnić, ponieważ maksymalny rozmiar dowolnego obiektu musi być mniejszy niż SIZE_MAX.
12431234123412341234123
@ 12431234123412341234123 Prawda o rozmiarze tablicy <= SIZE_MAX, ale tutaj nie ma tablic . Wskaźnik zwrócony z calloc()może wskazywać przydzieloną pamięć niż przekracza SIZE_MAX. Wiele implementacji ogranicza iloczyn 2 argumentów calloc()do SIZE_MAX, ale specyfikacja C nie narzuca tego limitu.
chux - Przywróć Monikę
21

z artykułu Benchmarkingowa zabawa z calloc () i zerowymi stronami na blogu Georga Hagera

Podczas przydzielania pamięci za pomocą calloc () żądana ilość pamięci nie jest przydzielana od razu. Zamiast tego wszystkie strony należące do bloku pamięci są połączone z jedną stroną zawierającą wszystkie zera za pomocą magii MMU (linki poniżej). Jeśli takie strony są tylko odczytywane (co było prawdziwe w przypadku tablic b, cid w oryginalnej wersji testu porównawczego), dane są dostarczane z pojedynczej strony zerowej, która - oczywiście - mieści się w pamięci podręcznej. Tyle o jądrach pętli związanych z pamięcią. Jeśli strona zostanie zapisana (nie ważne jak), wystąpi błąd, strona „prawdziwa” zostanie zmapowana, a strona zerowa zostanie skopiowana do pamięci. Nazywa się to kopiowaniem przy zapisie, znanym podejściem optymalizacyjnym (którego nauczyłem się wiele razy podczas wykładów w C ++). Po tym,

Ashish Chavan
źródło
gdzie jest link?
Rupesh Yadav.
2
pierwsza linia odpowiedzi zawiera link do bloga Georga Hagera.
Ashish Chavan
11

calloc jest ogólnie malloc+memset 0

Zasadniczo lepiej jest używać malloc+memsetjawnie, szczególnie gdy robisz coś takiego:

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

Jest to lepsze, ponieważ sizeof(Item)jest znane kompilatorowi w czasie kompilacji, a kompilator w większości przypadków zastąpi go najlepszymi możliwymi instrukcjami zerowania pamięci. Z drugiej strony, jeśli memsetma miejsce calloc, wielkość parametru alokacji nie jest wkompilowana w callockod i memsetczęsto wywoływana jest wartość rzeczywista , która zwykle zawiera kod do wypełniania bajt po bajcie aż do długiej granicy, niż cykl do wypełnienia pamięć w sizeof(long)kawałkach i na końcu bajt po bajcie wypełnia pozostałe miejsce. Nawet jeśli alokator jest wystarczająco inteligentny, aby do niektórych zadzwonić aligned_memset, nadal będzie to ogólna pętla.

Jednym godnym uwagi wyjątkiem byłby robący malloc / calloc bardzo duży fragment pamięci (niektóre power_of_two kilobajtów), w którym to przypadku alokacja może być dokonana bezpośrednio z jądra. Ponieważ jądra systemu operacyjnego zwykle zerują całą pamięć, którą oddają ze względów bezpieczeństwa, wystarczająco inteligentny calloc może po prostu zwrócić go z dodatkowym zerowaniem. Znowu - jeśli tylko przydzielasz coś, o czym wiesz, że jest małe, możesz lepiej skorzystać z malloc + memset pod względem wydajności.

virco
źródło
+1 za przypomnienie, że ogólna implementacja funkcji w bibliotece systemowej niekoniecznie jest szybsza niż ta sama operacja w kodzie użytkownika.
Patrick Schlüter,
1
Jest też drugi punkt, który sprawia, że calloc()wolniej niż malloc(): pomnożenie wielkości. calloc()jest wymagane użycie ogólnego mnożenia (jeśli size_tjest to 64 bity, nawet bardzo kosztowne 64 bity * 64 bity = operacja 64 bity), podczas gdy malloc () często ma stałą czasową kompilacji.
Patrick Schlüter,
4
glibc calloc ma kilka sprytnych decyzji, jak najskuteczniej wyczyścić zwrócony fragment, np. czasami tylko jego część wymaga wyczyszczenia, a także rozwiniętego wyczyszczenia do 9 * sizeof (size_t). Pamięć jest pamięcią, wyczyszczenie jej 3 bajtów naraz nie będzie szybsze tylko dlatego, że będziesz używać jej do przechowywania struct foo { char a,b,c; };. callocjest zawsze lepsze niż malloc+ memset, jeśli zawsze zamierzasz wyczyścić cały mallocregion ed. callocma również staranne, ale skuteczne sprawdzenie przepełnienia int w elementach size *.
Peter Cordes,
8

Różnica 1:

malloc() zwykle przydziela blok pamięci i jest to zainicjowany segment pamięci.

calloc() przydziela blok pamięci i inicjuje cały blok pamięci na 0.

Różnica 2:

Jeśli weźmiesz pod uwagę malloc() składnię, zajmie to tylko 1 argument. Rozważ następujący przykład poniżej:

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

Przykład: jeśli chcesz przydzielić 10 bloków pamięci dla typu int,

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

Jeśli weźmiesz pod uwagę calloc() składnię, zajmie 2 argumenty. Rozważ następujący przykład poniżej:

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

Przykład: jeśli chcesz przydzielić 10 bloków pamięci dla typu int i Zainicjować to wszystko do ZERO,

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

Podobieństwo:

Oba malloc()i calloc()domyślnie zwrócą void *, jeśli nie są rzutowane na typ.

Shivaraj Bhat
źródło
I dlaczego wyróżniasz typ danych i typ cast?
Wyprzedane
7

Istnieją dwie różnice.
Po pierwsze, jest w liczbie argumentów. malloc()pobiera pojedynczy argument (wymagana pamięć w bajtach), podczas gdy calloc()potrzebuje dwóch argumentów.
Po drugie, malloc()nie inicjuje przydzielonej pamięci, natomiast calloc()inicjuje przydzieloną pamięć do ZERO.

  • calloc()przydziela obszar pamięci, długość będzie iloczynem jego parametrów. callocwypełnia pamięć ZERO i zwraca wskaźnik do pierwszego bajtu. Jeśli nie uda się zlokalizować wystarczającej ilości miejsca, zwraca NULLwskaźnik.

Składnia: ptr_var=(cast_type *)calloc(no_of_blocks , size_of_each_block); tjptr_var=(type *)calloc(n,s);

  • malloc()przydziela pojedynczy blok pamięci REQUSTED SIZE i zwraca wskaźnik do pierwszego bajtu. Jeśli nie uda się zlokalizować wymaganej ilości pamięci, zwróci wskaźnik zerowy.

Składnia: funkcja ma jeden argument, który jest liczba bajtów przydzielić, podczas gdy funkcja przyjmuje dwa argumenty, jeden jest liczbą elementów, a drugi to liczba bajtów do przydzielenia dla każdego z tych elementów. Również inicjuje przydzieloną przestrzeń do zera, natomiast nie.ptr_var=(cast_type *)malloc(Size_in_bytes);malloc()calloc()calloc()malloc()

Jainendra
źródło
6

calloc()Funkcja, która jest zadeklarowana w <stdlib.h>nagłówku oferuje kilka zalet w stosunku do malloc()funkcji.

  1. Przydziela pamięć jako liczbę elementów o danym rozmiarze i
  2. Inicjuje przydzieloną pamięć, dzięki czemu wszystkie bity mają wartość zero.
Vipin Diwakar
źródło
6

malloc()i calloc()są funkcjami ze standardowej biblioteki C, które umożliwiają dynamiczny przydział pamięci, co oznacza, że ​​oba umożliwiają przydział pamięci podczas działania.

Ich prototypy są następujące:

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

Istnieją głównie dwie różnice między nimi:

  • Zachowanie: malloc()przydziela blok pamięci, nie inicjując go, a odczytanie zawartości tego bloku spowoduje odśmiecenie wartości. calloc(), z drugiej strony, alokuje blok pamięci i inicjuje go na zera, i oczywiście czytanie zawartości tego bloku spowoduje zera.

  • Składnia: malloc()pobiera 1 argument (wielkość do przydzielenia) i calloc()przyjmuje dwa argumenty (liczbę bloków do przydzielenia i rozmiar każdego bloku).

Zwracana wartość z obu jest wskaźnikiem do przydzielonego bloku pamięci, jeśli się powiedzie. W przeciwnym razie wartość NULL zostanie zwrócona, wskazując błąd alokacji pamięci.

Przykład:

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));

Ta sama funkcjonalność, którą calloc()można uzyskać za pomocą malloc()i 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)); 

Pamiętaj, że malloc()najlepiej jest zużyć, calloc()ponieważ jest szybszy. Jeśli chcesz zainicjować wartości zerowe, użyj calloc()zamiast tego.

elmiomar
źródło
5

Różnica, o której jeszcze nie wspomniano: limit rozmiaru

void *malloc(size_t size)może przydzielić tylko do SIZE_MAX.

void *calloc(size_t nmemb, size_t size); może przydzielić około SIZE_MAX*SIZE_MAX .

Ta zdolność nie jest często wykorzystywana na wielu platformach z adresowaniem liniowym. Takie systemy ograniczają calloc()się do nmemb * size <= SIZE_MAX.

Weźmy pod uwagę typ 512 bajtów, disk_sectora kod chce użyć wielu sektorów. Tutaj kod może używać tylko do SIZE_MAX/sizeof disk_sectorsektorów.

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

Zastanów się, co pozwala na jeszcze większy przydział.

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

Teraz, jeśli taki system może zapewnić tak duży przydział, to inna sprawa. Większość dzisiaj tego nie zrobi. Jednak miało to miejsce od wielu lat, kiedy SIZE_MAXbyło 65535. Biorąc pod uwagę prawo Moore'a , podejrzewają, że nastąpi to około 2030 r. W przypadku niektórych modeli SIZE_MAX == 4294967295pamięci i pul pamięci w 100 GB.

chux - Przywróć Monikę
źródło
2
Ogólnie rzecz biorąc, size_t będzie w stanie pomieścić największy obiekt, jaki program może obsłużyć. System, w którym size_t ma 32 bity, prawdopodobnie nie będzie w stanie obsłużyć alokacji większej niż 4294967295 bajtów, a system, który byłby w stanie obsłużyć alokacje o tym rozmiarze, prawie na pewno zwiększyłby size_tliczbę bitów do 32. Jedynym pytaniem jest, czy można polegać na callocwartościach, których produkt przekracza, SIZE_MAXaby uzyskać zero, zamiast zwracać wskaźnik do mniejszej alokacji.
supercat
Zgadzam się na twoje uogólnienie , ale specyfikacja C pozwala na calloc()przekroczenie przydziałów SIZE_MAX. Zdarzyło się to w przeszłości z 16-bitową size_twersją, a ponieważ pamięć wciąż się tani, nie widzę powodu, dla którego nie będzie się działo, nawet jeśli nie jest to powszechne .
chux - Przywróć Monikę
1
Standard C umożliwia kodowi żądanie przydziału, którego rozmiar przekracza SIZE_MAX. Z pewnością nie wymaga to żadnych okoliczności, w których taki przydział mógłby się powieść; Nie jestem pewien, czy istnieje jakaś szczególna korzyść z mandatu, że implementacje, które nie są w stanie obsłużyć takich alokacji, muszą zwracać NULL(zwłaszcza biorąc pod uwagę, że niektóre implementacje mają mallocwskaźniki powrotu do miejsca, które nie zostało jeszcze zatwierdzone i może nie być dostępne, gdy kod faktycznie próbuje użyć to).
supercat
Co więcej, tam gdzie w przeszłości mogły istnieć systemy, których dostępny zakres adresowania przekraczał największą możliwą do przedstawienia liczbę całkowitą, nie widzę żadnej realnej możliwości, że kiedykolwiek się powtórzy, ponieważ wymagałoby to pojemności miliardów gigabajtów. Nawet gdyby Prawo Moore'a nadal obowiązywało, przejście od punktu, w którym 32 bity przestały wystarczać, do punktu, w którym 64 bity przestały wystarczać, zajęłoby dwa razy więcej niż przejście od miejsca, w którym 16 bitów wystarczyło do punktu, w którym 32 „t.
supercat
1
Dlaczego miałby to realizacja, która może pomieścić jeden przydział w nadmiarze 4G nie definiują size_tsię uint64_t?
supercat
2

Liczba bloków:
malloc () przypisuje pojedynczy blok żądanej pamięci,
calloc () przypisuje wiele bloków żądanej pamięci

Inicjalizacja:
malloc () - nie usuwa i nie inicjuje przydzielonej pamięci.
calloc () - inicjuje przydzieloną pamięć o zero.

Prędkość:
malloc () jest szybki.
calloc () jest wolniejszy niż malloc ().

Argumenty i składnia:
malloc () przyjmuje 1 argument:

  1. bajty

    • Liczba bajtów do przydzielenia

calloc () przyjmuje 2 argumenty:

  1. długość

    • liczba bloków pamięci do przydzielenia
  2. bajty
    • liczba bajtów do przydzielenia w każdym bloku pamięci
void *malloc(size_t bytes);         
void *calloc(size_t length, size_t bytes);      

Sposób przydziału pamięci:
Funkcja malloc przypisuje pamięć o pożądanym „rozmiarze” z dostępnej sterty.
Funkcja calloc przypisuje pamięć o rozmiarze równym „num * size”.

Znaczenie w nazwie:
Nazwa malloc oznacza „przydział pamięci”.
Nazwa calloc oznacza „ciągły przydział”.

Majbah Habib
źródło