@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ę.
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.
* 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
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ć:
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.
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.
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.
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.
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,
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.
+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:
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.
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()
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.
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.
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.
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.
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:
bajty
Liczba bajtów do przydzielenia
calloc () przyjmuje 2 argumenty:
długość
liczba bloków pamięci do przydzielenia
bajty
liczba bajtów do przydzielenia w każdym bloku pamięci
malloc
ptr = calloc(MAXELEMS, sizeof(*ptr));
Odpowiedzi:
calloc()
daje bufor inicjowany zerem,malloc()
pozostawiając pamięć niezainicjowaną.W przypadku dużych alokacji większość
calloc
implementacji w głównych systemach operacyjnych otrzyma strony o znanym wyzerowaniu z systemu operacyjnego (np. Przez POSIXmmap(MAP_ANONYMOUS)
lub WindowsVirtualAlloc
), więc nie trzeba ich zapisywać w przestrzeni użytkownika. W ten sposób normalnymalloc
pobiera także więcej stron z systemu operacyjnego;calloc
korzysta po prostu z gwarancji systemu operacyjnego.Oznacza to, że
calloc
pamięć 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,
malloc
aby (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
calloc
mogą pozostawić tocalloc
zeru 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.źródło
calloc
niekoniecznie 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 zrobiszcalloc
, najpierw próbuje znaleźć jeden z takich zerowanych bloków i po prostu dać ci go - i najprawdopodobniej go znajdzie.Mniej znaną różnicą jest to, że w systemach operacyjnych z optymistycznym przydziałem pamięci, takich jak Linux, zwracany wskaźnik
malloc
nie jest wspierany przez rzeczywistą pamięć, dopóki program jej nie dotknie.calloc
rzeczywiś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
źródło
calloc
nie 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 wymagacalloc
dostrojenia do systemu operacyjnego, a nie ogólnej funkcji bibliotekimalloc
. Lub implementator możecalloc
porównać każde słowo z zerem przed jego wyzerowaniem. Nie zaoszczędziłoby to czasu, ale uniknęłoby zabrudzenia nowych stron.dlmalloc
implementacje pomijają,memset
jeśli fragment został uzyskany poprzez wprowadzeniemmap
nowych 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.omalloc
również pomijamemset
;calloc
nigdy nie musi dotykać stron, które nie są jeszcze używane przez aplikację (pamięć podręczna stron). Jednak niezwykle prymitywnecalloc
implementacje różnią się.Jedną z często pomijanych zalet
calloc
jest to, że (zgodne implementacje) pomoże chronić cię przed podatnością na przepełnienie liczb całkowitych. Porównać:vs.
To pierwsze może spowodować niewielką alokację i kolejne przepełnienia bufora, jeśli
count
jest 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ódło
char
jest nie przepełnienie lecz Konwersja realizacja zdefiniowane przy przypisywaniu tyłu wynik wchar
obiekcie.size_t
64-bitowy, więc nie ma problemu”, jest to błędny sposób myślenia, który doprowadzi do błędów bezpieczeństwa.size_t
jest typem abstrakcyjnym, który reprezentuje rozmiary, i nie ma powodu, aby sądzić, że arbitralny iloczyn liczby 32-bitowej isize_t
(uwaga:sizeof *bar
może w zasadzie być większy niż 2 ^ 32 w 64-bitowej implementacji C!) pasujesize_t
.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/
źródło
Nie ma różnicy w wielkości przydzielonego bloku pamięci.
calloc
po 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 zcalloc
mają wartość początkową, tak jakby były inicjowane dosłownie0
, tzn. Liczby całkowite powinny mieć wartość0
zmiennych zmiennoprzecinkowych - wartość0.0
wskaź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 typuunsigned 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 char
wspomniany 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
(imemset(..., 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
calloc
dział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ąć,malloc
zamiast tego użyć i wykonać własną inicjalizację.W końcu, kolejnym ważnym szczegółem jest to
calloc
jest wymagane do obliczenia ostatecznego rozmiaru bloku wewnętrznie przez pomnożenie wielkości elementu liczby elementów. Robiąc to,calloc
należ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 twojamalloc
wersja nie próbuje wykryć przepełnienia. Przydzieli pewną „nieprzewidywalną” ilość pamięci na wypadek przepełnienia.źródło
memset(p, v, n * sizeof type);
stanowić problem, ponieważn * sizeof type
może się przepełnić. Chyba będę musiał użyćfor(i=0;i<n;i++) p[i]=v;
pętli dla solidnego kodu.n
elementami, w której element ma rozmiarsizeof type
, ton*sizeof type
nie może się przepełnić, ponieważ maksymalny rozmiar dowolnego obiektu musi być mniejszy niżSIZE_MAX
.SIZE_MAX
, ale tutaj nie ma tablic . Wskaźnik zwrócony zcalloc()
może wskazywać przydzieloną pamięć niż przekraczaSIZE_MAX
. Wiele implementacji ogranicza iloczyn 2 argumentówcalloc()
doSIZE_MAX
, ale specyfikacja C nie narzuca tego limitu.z artykułu Benchmarkingowa zabawa z calloc () i zerowymi stronami na blogu Georga Hagera
źródło
calloc
jest ogólniemalloc+memset
0Zasadniczo lepiej jest używać
malloc+memset
jawnie, szczególnie gdy robisz coś takiego: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ślimemset
ma miejscecalloc
, wielkość parametru alokacji nie jest wkompilowana wcalloc
kod imemset
czę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ęć wsizeof(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.
źródło
calloc()
wolniej niżmalloc()
: pomnożenie wielkości.calloc()
jest wymagane użycie ogólnego mnożenia (jeślisize_t
jest to 64 bity, nawet bardzo kosztowne 64 bity * 64 bity = operacja 64 bity), podczas gdy malloc () często ma stałą czasową kompilacji.struct foo { char a,b,c; };
.calloc
jest zawsze lepsze niżmalloc
+memset
, jeśli zawsze zamierzasz wyczyścić całymalloc
region ed.calloc
ma również staranne, ale skuteczne sprawdzenie przepełnienia int w elementach size *.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:Przykład: jeśli chcesz przydzielić 10 bloków pamięci dla typu int,
Jeśli weźmiesz pod uwagę
calloc()
składnię, zajmie 2 argumenty. Rozważ następujący przykład poniżej:Przykład: jeśli chcesz przydzielić 10 bloków pamięci dla typu int i Zainicjować to wszystko do ZERO,
Podobieństwo:
Oba
malloc()
icalloc()
domyślnie zwrócą void *, jeśli nie są rzutowane na typ.źródło
Istnieją dwie różnice.
Po pierwsze, jest w liczbie argumentów.
malloc()
pobiera pojedynczy argument (wymagana pamięć w bajtach), podczas gdycalloc()
potrzebuje dwóch argumentów.Po drugie,
malloc()
nie inicjuje przydzielonej pamięci, natomiastcalloc()
inicjuje przydzieloną pamięć do ZERO.calloc()
przydziela obszar pamięci, długość będzie iloczynem jego parametrów.calloc
wypełnia pamięć ZERO i zwraca wskaźnik do pierwszego bajtu. Jeśli nie uda się zlokalizować wystarczającej ilości miejsca, zwracaNULL
wskaź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()
źródło
calloc()
Funkcja, która jest zadeklarowana w<stdlib.h>
nagłówku oferuje kilka zalet w stosunku domalloc()
funkcji.źródło
malloc()
icalloc()
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:
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) icalloc()
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:
Ta sama funkcjonalność, którą
calloc()
można uzyskać za pomocąmalloc()
imemset()
:Pamiętaj, że
malloc()
najlepiej jest zużyć,calloc()
ponieważ jest szybszy. Jeśli chcesz zainicjować wartości zerowe, użyjcalloc()
zamiast tego.źródło
Różnica, o której jeszcze nie wspomniano: limit rozmiaru
void *malloc(size_t size)
może przydzielić tylko doSIZE_MAX
.void *calloc(size_t nmemb, size_t size);
może przydzielić okołoSIZE_MAX*SIZE_MAX
.Ta zdolność nie jest często wykorzystywana na wielu platformach z adresowaniem liniowym. Takie systemy ograniczają
calloc()
się donmemb * size <= SIZE_MAX
.Weźmy pod uwagę typ 512 bajtów,
disk_sector
a kod chce użyć wielu sektorów. Tutaj kod może używać tylko doSIZE_MAX/sizeof disk_sector
sektorów.Zastanów się, co pozwala na jeszcze większy przydział.
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_MAX
było 65535. Biorąc pod uwagę prawo Moore'a , podejrzewają, że nastąpi to około 2030 r. W przypadku niektórych modeliSIZE_MAX == 4294967295
pamięci i pul pamięci w 100 GB.źródło
size_t
liczbę bitów do 32. Jedynym pytaniem jest, czy można polegać nacalloc
wartościach, których produkt przekracza,SIZE_MAX
aby uzyskać zero, zamiast zwracać wskaźnik do mniejszej alokacji.calloc()
przekroczenie przydziałówSIZE_MAX
. Zdarzyło się to w przeszłości z 16-bitowąsize_t
wersją, 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 .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ąmalloc
wskaź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).size_t
sięuint64_t
?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:
bajty
calloc () przyjmuje 2 argumenty:
długość
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ł”.
źródło