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?
ptr = calloc(MAXELEMS, sizeof(*ptr));
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?
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 POSIX mmap(MAP_ANONYMOUS)
lub Windows VirtualAlloc
), więc nie trzeba ich zapisywać w przestrzeni użytkownika. W ten sposób normalny malloc
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ć to calloc
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.
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 zrobisz calloc
, 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
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 wymaga calloc
dostrojenia do systemu operacyjnego, a nie ogólnej funkcji biblioteki malloc
. Lub implementator może calloc
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 wprowadzenie mmap
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ż pomija memset
; calloc
nigdy nie musi dotykać stron, które nie są jeszcze używane przez aplikację (pamięć podręczna stron). Jednak niezwykle prymitywne calloc
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ć:
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 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.
char
jest nie przepełnienie lecz Konwersja realizacja zdefiniowane przy przypisywaniu tyłu wynik w char
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 i size_t
(uwaga: sizeof *bar
może w zasadzie być większy niż 2 ^ 32 w 64-bitowej implementacji C!) pasuje size_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/
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 z calloc
mają wartość początkową, tak jakby były inicjowane dosłownie 0
, 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 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 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
(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 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 twoja malloc
wersja nie próbuje wykryć przepełnienia. Przydzieli pewną „nieprzewidywalną” ilość pamięci na wypadek przepełnienia.
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 rozmiar sizeof type
, to n*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 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.
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,
calloc
jest ogólnie malloc+memset
0
Zasadniczo lepiej jest używać malloc+memset
jawnie, 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 memset
ma miejsce calloc
, wielkość parametru alokacji nie jest wkompilowana w calloc
kod i memset
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ęć 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.
calloc()
wolniej niż malloc()
: pomnożenie wielkości. calloc()
jest wymagane użycie ogólnego mnożenia (jeśli size_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ły malloc
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:
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.
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. calloc
wypełnia pamięć ZERO i zwraca wskaźnik do pierwszego bajtu. Jeśli nie uda się zlokalizować wystarczającej ilości miejsca, zwraca NULL
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()
calloc()
Funkcja, która jest zadeklarowana w <stdlib.h>
nagłówku oferuje kilka zalet w stosunku do malloc()
funkcji.
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.
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_sector
a kod chce użyć wielu sektorów. Tutaj kod może używać tylko do SIZE_MAX/sizeof disk_sector
sektoró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_MAX
był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 == 4294967295
pamięci i pul pamięci w 100 GB.
size_t
liczbę bitów do 32. Jedynym pytaniem jest, czy można polegać na calloc
wartościach, których produkt przekracza, SIZE_MAX
aby uzyskać zero, zamiast zwracać wskaźnik do mniejszej alokacji.
calloc()
przekroczenie przydziałów SIZE_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ść
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ł”.
malloc