Jaki jest sens malloc (0)?


Odpowiedzi:


121

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;

51
Osobiście uważam, że ustawienie na NULL jest lepszą strategią międzyplatformową, ponieważ funkcja free () jest gwarantowana (zgodnie ze specyfikacją), aby działała dobrze na NULL jako dane wejściowe.
Reed Copsey

2
Jak wspomniał C. Ross, technicznie rzecz biorąc, niektóre platformy mogą zwracać tutaj wskaźnik (to jest „unikalny wskaźnik, który można przekazać do zwolnienia”), ale jeśli traktujesz to jako znak *, może to dać nieprawidłowy, niezakończony znak. Poleganie na tym w sytuacjach międzyplatformowych może być niebezpieczne.
Reed Copsey

9
Naprawdę chciałbym, żeby specyfikacja również mówiła „bezpiecznie przekazano do ponownego przydzielenia” -.-
hanshenrik

1
@NSAddict "pusta struktura, gdzie sizeof zwróci 0", podaj przykład, brzmi jak rozszerzenie języka.
chux - Przywróć Monikę

1
@hanshenrik Kto powiedział, że nie możesz? realloc()umożliwia przekazanie dowolnego prawidłowego wskaźnika zwróconego przez malloc(). Powinno wystarczyć.
glglgl

51

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ć NULLlub 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 , a
  • free(0) jest zły.

Więc upewnił się, że ta artisti inne zmienne zawsze mają jakąś „ważną” wartość. Komentarz mówi tyle: // these must always point at malloc'd data.


10
Fakt, że jest on zależny od implementacji, czyni go mniej lub bardziej bezużytecznym - to jeden z bardziej bzdurnych fragmentów standardu C, a spora część komitetu ds. Standardów (np. PJ Plauger) narzekało na to.

10
Zgadzam się. Jeśli malloc(0)zwracany jest prawidłowy wskaźnik, to malloc()zwracanie NULLzawsze oznacza „błąd” i 0nie jest już przypadkiem specjalnym, który jest bardziej spójny.
Alok Singhal,

1
Ponieważ okoliczności mallocniepowodzenia 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. :-)
R .. GitHub STOP HELPING ICE

6
Czy możesz realloczwrócić wskaźnik malloc(0)? Potrafisz realloc((char*)NULL)?
Braden Best

3
@Braden Best Yes dla obu.
chux - Przywróć Monikę

11

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ć.


9
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.
Alok Singhal,

Implementacje Malloc przeprowadzają przechowywanie rekordów, co może dodać pewną ilość danych na wskaźnik zwracany ponad żądany rozmiar.
user7116

1
Zużyta pamięć i przydzielona pamięć nie oznaczają tego samego. W tym przypadku większość implementacji zwróci unikalny wskaźnik. Oznacza to, że część przestrzeni adresowej musi zostać poświęcona dla tego wskaźnika. W zależności od alokatora może to faktycznie oznaczać, że przydzieli 1 bajt lub więcej.
Coincoin

1
Biblioteka może robić, co chce - cóż, może albo zwrócić unikalny wskaźnik, którego nikt inny malloc()nie zwróci, albo zwróci NULL.
Alok Singhal,

1
@jldupont: przynajmniej biblioteka Microsoft C Run-Time zwraca unikatowy wskaźnik dla malloc(0). Jednak w tej samej implementacji standardowej biblioteki C realloc(ptr, 0)zwalnia ptri zwraca NULL.
Medinoc

5

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.


4

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 artisttak 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.


3

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 NULLwskaźnik.

W konsekwencji pisanie nie artist = malloc(0); jest w żaden sposób lepsze niż pisanie artist = NULL;


1
Szkoda, że ​​implementacja nie może zwrócić niezerowego, nieunikalnego wskaźnika. W ten sposób malloc(0)może zwrócić, powiedzmy, 0x1 i free()może mieć specjalne sprawdzenie 0x1, tak samo jak w przypadku 0x0.
Todd Lehman

3
@Todd Lehman Implementacja może zrobić to, co sugerujesz. Specyfikacja C nie określa, że ​​wynikiem musi być „ NULLlub 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.
chux - Przywróć Monikę

manró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.
Lundin

@Lundin True. Ale strony podręcznika systemowego są znacznie bardziej dostępne niż standard C, a strony podręcznika systemowego w systemach GNU / Linux ogólnie całkiem dobrze dokumentują, który standard (y) są stosowane w implementacji. Wraz z informacją, które części są zgodne z jaką normą, jeśli się różnią. Mam wrażenie, że oboje chcą być precyzyjni i reklamować każdy pojedynczy bit będący rozszerzeniem GNU ...
cmaster - przywróć monikę

3

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.



2

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:

  • foo ma wartość różną od NULL i rozmiar wynosi zero, realloc(foo, size); . Kiedy przekazujesz wskaźnik inny niż NULL i rozmiar zerowy do ponownego przydzielenia, realloc zachowuje się tak, jakbyś wywołał free (…)
  • foo ma wartość NULL, a rozmiar jest różny od zera i większy niż 1 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.


1

Aby właściwie odpowiedzieć na postawione pytanie: nie ma powodu, aby to robić


0

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


1
Pisanie do *imoże się nie zawiesić w Twoim przypadku, ale mimo to jest to niezdefiniowane zachowanie. Uważaj na nosowe demony!
John Dvorak

1
Tak. To prawda. Jest to specyficzne dla implementacji. Sprawdziłem to na MaxOS X i niektórych dystrybucjach Linuksa. Nie próbowałem tego na innych platformach. Powiedziawszy to, koncepcja, którą opisałem, została opisana w książce „Język programowania C” autorstwa Brain Kernighan i Dennisa Ritchiego.
Sagar Bhosale,

Wiem: super późny komentarz do tego pytania. Ale jest czasami zastosowanie, na 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.
Jesse Chisholm

Czytałem „Język programowania C” autorstwa Brain Kernighan i Dennisa Ritchiego i nie pamiętam, żeby o tym mówił malloc(0). Czy możesz powiedzieć, do którego rozdziału również się odnosisz? Przydałoby się też podanie dokładnej wyceny.
Андрей Беньковский

0

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.
  • mallocostatecznie wywołuje HeapAllocprzy użyciu domyślnego stosu środowiska wykonawczego C, który następnie wywołuje RtlAllocateHeapitp.
  • free(p);używa HeapFreedo zwolnienia bufora o długości 0 na stercie. Brak zwolnienia spowodowałby wyciek pamięci.

0

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.



-2

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.


-3

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 ptrnie jest null.


-6

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 gcckompilatorze 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 mallocrzutowania, 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.


5
Krellan ma rację, wskazując, że ta odpowiedź jest błędna: 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.
cmaster

-6

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 artistz NULL. if (artist == NULL)jest fałszywe i if (artist)prawdziwe.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.