Odpowiedzi:
Zgodnie ze specyfikacją malloc (0) zwróci „pusty wskaźnik lub unikalny wskaźnik, który można pomyślnie przekazać do funkcji free ()”.
Zasadniczo pozwala to niczego nie przydzielać, ale nadal bez obaw przekazywać zmienną „artist” do wywołania funkcji free (). Ze względów praktycznych jest to prawie to samo, co robienie:
artist = NULL;
Norma C (C17 7.22.3 / 1) mówi:
Jeśli rozmiar żądanej przestrzeni wynosi zero, zachowanie jest zdefiniowane w implementacji: zwracany jest wskaźnik pusty lub zachowanie jest tak, jakby rozmiar był wartością niezerową, z wyjątkiem tego, że zwracany wskaźnik nie powinien być używany do uzyskiwania dostępu do obiektu.
malloc(0)
Może więc zwrócić NULL
lub prawidłowy wskaźnik, którego nie można wyłuskać . W obu przypadkach dzwonienie jest całkowicie uzasadnionefree()
.
Naprawdę nie myślę malloc(0)
ma zbyt wiele zastosowania, z wyjątkiem przypadków, gdy malloc(n)
na przykład jest wywoływana w pętli in
może wynosić zero.
Patrząc na kod w linku uważam, że autor miał dwa błędne przekonania:
malloc(0)
zwraca prawidłowy wskaźnik zawsze , afree(0)
jest zły.Więc upewnił się, że ta artist
i inne zmienne zawsze mają jakąś „ważną” wartość. Komentarz mówi tyle: // these must always point at malloc'd data
.
malloc(0)
zwracany jest prawidłowy wskaźnik, to malloc()
zwracanie NULL
zawsze oznacza „błąd” i 0
nie jest już przypadkiem specjalnym, który jest bardziej spójny.
malloc
niepowodzenia w uzyskaniu pamięci są zdefiniowane przez implementację, implementacja może po prostu zdefiniować, że alokacje rozmiaru 0 są zawsze niezadowalające ( ENOMEM
), a teraz malloc(0)
zwracanie 0 (z errno==ENOMEM
) jest spójne. :-)
realloc
zwrócić wskaźnik malloc(0)
? Potrafisz realloc((char*)NULL)
?
Zachowanie malloc (0) jest specyficzne dla implementacji. Biblioteka może zwracać NULL lub mieć zwykłe zachowanie malloc bez przydzielonej pamięci. Cokolwiek to robi, musi być gdzieś udokumentowane.
Zwykle zwraca wskaźnik, który jest poprawny i unikalny, ale NIE powinien być wyłuskiwany. Należy również pamiętać, że MOŻE zużywać pamięć, mimo że w rzeczywistości niczego nie przydzielał.
Możliwe jest ponowne przydzielenie niezerowego wskaźnika malloc (0).
Posiadanie malloc (0) dosłownie nie jest jednak zbyt przydatne. Jest używany głównie, gdy alokacja dynamiczna ma zerowy bajt i nie chcesz jej zweryfikować.
malloc()
musi gdzieś przechowywać „informacje porządkowe” (na przykład ten rozmiar przydzielonego bloku i inne dane pomocnicze). Tak więc, jeśli malloc(0)
nie zwróci NULL
, użyje pamięci do przechowywania tych informacji, a jeśli nie free()
d, będzie stanowić wyciek pamięci.
malloc()
nie zwróci, albo zwróci NULL
.
malloc(0)
. Jednak w tej samej implementacji standardowej biblioteki C realloc(ptr, 0)
zwalnia ptr
i zwraca NULL.
W innym miejscu na tej stronie jest odpowiedź, która zaczyna się od „malloc (0) zwróci prawidłowy adres pamięci i którego zakres będzie zależał od typu wskaźnika, do którego jest przydzielana pamięć”. To stwierdzenie jest nieprawidłowe (nie mam wystarczającej reputacji, aby bezpośrednio skomentować tę odpowiedź, więc nie mogę umieścić tego komentarza bezpośrednio pod tym).
Wykonanie malloc (0) nie przydzieli automatycznie pamięci o odpowiednim rozmiarze. Funkcja malloc nie jest świadoma tego, do czego rzutujesz jej wynik. Funkcja malloc opiera się wyłącznie na numerze rozmiaru, który podajesz jako argument. Musisz zrobić malloc (sizeof (int)), aby uzyskać wystarczającą ilość miejsca do przechowywania int, na przykład, a nie 0.
malloc(0)
nie ma to dla mnie sensu, chyba że kod opiera się na zachowaniu specyficznym dla implementacji. Jeśli kod ma być przenośny, musi uwzględniać fakt, że NULL zwrot z adresu malloc(0)
nie jest błędem. Dlaczego więc nie przypisać NULL artist
tak czy owak, skoro jest to poprawny, pomyślny wynik, zawiera mniej kodu i nie spowoduje, że programiści zajmujący się konserwacją będą potrzebowali czasu na jego rozgryzienie?
malloc(SOME_CONSTANT_THAT_MIGHT_BE_ZERO)
lub malloc(some_variable_which_might_be_zero)
może mieć ich zastosowania, chociaż znowu musisz uważać, aby nie traktować zwrotu NULL jako niepowodzenia, jeśli wartość wynosi 0, ale rozmiar 0 powinien być OK.
Jest tu wiele półprawdziwych odpowiedzi, więc oto twarde fakty. Strona malloc()
podręcznika dla mówi:
Jeśli rozmiar wynosi 0, malloc () zwraca albo NULL, albo unikalną wartość wskaźnika, którą można później pomyślnie przekazać do funkcji free ().
Oznacza to, że nie ma absolutnie żadnej gwarancji, że wynik malloc(0)
jest unikalny lub NULL. Jedyną gwarancją jest definicja free()
, a oto, co mówi strona podręcznika:
Jeśli ptr ma wartość NULL, nie jest wykonywana żadna operacja.
Więc cokolwiek malloc(0)
wróci, można to bezpiecznie przekazać free()
. Ale także NULL
wskaźnik.
W konsekwencji pisanie nie artist = malloc(0);
jest w żaden sposób lepsze niż pisanie artist = NULL;
malloc(0)
może zwrócić, powiedzmy, 0x1 i free()
może mieć specjalne sprawdzenie 0x1, tak samo jak w przypadku 0x0.
NULL
lub unikalny wskaźnik”. zamiast „pustego wskaźnika lub wskaźnika do przydzielonego miejsca”. Nie ma unikalnego wymagania. OTOH, zwracanie nieunikalnej wartości specjalnej może zakłócić kod, który liczy na unikalne wartości. Być może pytanie narożne dla SO.
man
równie dobrze może udokumentować zdefiniowaną w implementacji formę używaną w * nix. W tym przypadku tak nie jest, ale nadal nie jest to kanoniczne źródło dla generała C.
Dlaczego nie powinieneś tego robić ...
Ponieważ wartość zwracana przez malloc jest zależna od implementacji, możesz otrzymać wskaźnik NULL lub inny adres z powrotem. Może to doprowadzić do przepełnienia bufora sterty, jeśli kod obsługi błędów nie sprawdza zarówno rozmiaru, jak i zwracanej wartości, co prowadzi do problemów ze stabilnością (awarie) lub jeszcze gorszych problemów z bezpieczeństwem.
Rozważmy ten przykład, w którym dalszy dostęp do pamięci za pośrednictwem zwróconego adresu spowoduje uszkodzenie stosu iff wielkości sterty wynosi zero, a implementacja zwraca wartość inną niż NULL.
size_t size;
/* Initialize size, possibly by user-controlled input */
int *list = (int *)malloc(size);
if (list == NULL) {
/* Handle allocation error */
}
else {
/* Continue processing list */
}
Zobacz tę stronę Bezpieczne kodowanie z CERT Coding Standards, gdzie wziąłem powyższy przykład do dalszego czytania.
Trzeba przyznać, że nigdy wcześniej tego nie widziałem, po raz pierwszy widziałem tę składnię, można powiedzieć, klasyczny przypadek przesady funkcji. W połączeniu z odpowiedzią Reeda, chciałbym zwrócić uwagę, że jest coś podobnego, który wygląda jak przeciążona funkcja realloc
:
realloc(foo, size);
. Kiedy przekazujesz wskaźnik inny niż NULL i rozmiar zerowy do ponownego przydzielenia, realloc zachowuje się tak, jakbyś wywołał free (…)realloc(foo, size);
,. Kiedy przekazujesz wskaźnik NULL i rozmiar jest różny od zera, realloc zachowuje się tak, jakbyś wywołał malloc (…)Mam nadzieję, że to pomoże. Pozdrawiam Tom.
malloc (0) zwróci NULL lub prawidłowy wskaźnik, który można poprawnie przekazać do free. I chociaż wydaje się, że wspomnienie, na które wskazuje, jest bezużyteczne lub nie można go zapisać ani odczytać, nie zawsze jest to prawda. :)
int *i = malloc(0);
*i = 100;
printf("%d", *i);
Spodziewamy się tutaj błędu segmentacji, ale, co zaskakujące, drukuje 100! Dzieje się tak, ponieważ malloc w rzeczywistości prosi o ogromną porcję pamięci, gdy wywołujemy malloc po raz pierwszy. Każde kolejne wywołanie malloc wykorzystuje pamięć z tego dużego kawałka. Dopiero po zakończeniu tego ogromnego kawałka prosi się o nową pamięć.
Użycie malloc (0): jeśli jesteś w sytuacji, w której chcesz, aby kolejne wywołania malloc były szybsze, wywołanie malloc (0) powinno zrobić to za ciebie (z wyjątkiem przypadków skrajnych).
*i
może się nie zawiesić w Twoim przypadku, ale mimo to jest to niezdefiniowane zachowanie. Uważaj na nosowe demony!
malloc(0)
które nie jest wymienione. W tych implementacjach, w których zwraca wartość inną niż NULL, szczególnie w kompilacji DEBUG, prawdopodobnie przydziela WIĘCEJ, niż prosiłeś, i podaje wskaźnik do przekroczenia wewnętrznego nagłówka. Pozwala to zorientować się w rzeczywistym zużyciu pamięci, jeśli uzyskasz to przed i po serii alokacji. np .: void* before = malloc(0); ... void* after = malloc(0); long long total = after - before;
lub jakiś taki.
malloc(0)
. Czy możesz powiedzieć, do którego rozdziału również się odnosisz? Przydałoby się też podanie dokładnej wyceny.
W systemie Windows:
void *p = malloc(0);
przydzieli bufor o zerowej długości na lokalnym stercie. Zwrócony wskaźnik jest prawidłowym wskaźnikiem sterty.malloc
ostatecznie wywołuje HeapAlloc
przy użyciu domyślnego stosu środowiska wykonawczego C, który następnie wywołuje RtlAllocateHeap
itp.free(p);
używa HeapFree
do zwolnienia bufora o długości 0 na stercie. Brak zwolnienia spowodowałby wyciek pamięci.Jest to całkiem przydatne i (oczywiście IMHO) dozwolone zachowanie zwracania wskaźnika NULL jest zepsute. Dynamiczny wskaźnik jest przydatny nie tylko ze względu na to, na co wskazuje, ale także z tego, że jego adres jest unikalny. Zwrócenie wartości NULL usuwa tę drugą właściwość. Wszystkie wbudowane programy mallocs I (w rzeczywistości dość często) mają takie zachowanie.
Nie jestem pewien, zgodnie z jakimś przypadkowym kodem źródłowym malloc, który znalazłem, wejście 0 powoduje zwrócenie wartości NULL. Jest to więc szalony sposób ustawiania wskaźnika artysty na NULL.
http://www.raspberryginger.com/jbailey/minix/html/lib_2ansi_2malloc_8c-source.html
Oto analiza po uruchomieniu z narzędziem do sprawdzania pamięci Valgrind.
==16740== Command: ./malloc0
==16740==
p1 = 0x5204040
==16740==
==16740== HEAP SUMMARY:
==16740== in use at exit: 0 bytes in 0 blocks
==16740== total heap usage: 2 allocs, 2 frees, 1,024 bytes allocated
==16740==
==16740== All heap blocks were freed -- no leaks are possible
a oto mój przykładowy kod:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
//int i;
char *p1;
p1 = (char *)malloc(0);
printf("p1 = %p\n", p1);
free(p1);
return 0;
}
Domyślnie przydzielane jest 1024 bajty. Jeśli zwiększę rozmiar malloc, przydzielone bajty wzrosną o 1025 i tak dalej.
Zgodnie z odpowiedzią Reeda Copseya i stroną podręcznika man malloc, napisałem kilka przykładów do przetestowania. I odkryłem, że malloc (0) zawsze da mu unikalną wartość. Zobacz mój przykład:
char *ptr;
if( (ptr = (char *) malloc(0)) == NULL )
puts("Got a null pointer");
else
puts("Got a valid pointer");
Dane wyjściowe to „Masz prawidłowy wskaźnik”, co oznacza, że ptr
nie jest null.
malloc(0)
zwróci prawidłowy adres pamięci, którego zakres będzie zależał od typu wskaźnika, któremu jest przydzielana pamięć. Możesz także przypisać wartości do obszaru pamięci, ale powinno to być w zakresie z typem używanego wskaźnika. Możesz także zwolnić przydzieloną pamięć. Wyjaśnię to na przykładzie:
int *p=NULL;
p=(int *)malloc(0);
free(p);
Powyższy kod będzie działał dobrze w gcc
kompilatorze na komputerze z systemem Linux. Jeśli masz kompilator 32-bitowy, możesz podać wartości z zakresu liczb całkowitych, tj. Od -2147483648 do 2147483647. To samo dotyczy również znaków. Należy pamiętać, że jeśli typ zadeklarowanego wskaźnika zostanie zmieniony, zakres wartości zmieni się niezależnie od typu malloc
rzutowania, tj
unsigned char *p=NULL;
p =(char *)malloc(0);
free(p);
p
przyjmie wartość od 0 do 255 znaków char, ponieważ jest zadeklarowana jako int bez znaku.
malloc()
nie wie nic o obsadzie (co jest właściwie całkowicie zbędne w C). Wyłuskanie wartości zwracanej malloc(0)
spowoduje wywołanie niezdefiniowanego zachowania.
Aby poprawić fałszywe wrażenie:
artist = (char *) malloc(0);
nigdy przenigdy nie wróci NULL
; to nie to samo co artist = NULL;
. Napisz prosty program i porównaj artist
z NULL
. if (artist == NULL)
jest fałszywe i if (artist)
prawdziwe.