Dlaczego malloc inicjalizuje wartości na 0 w gcc?

Może różni się od platformy do platformy, ale

Kiedy kompiluję używając gcc i uruchamiam poniższy kod, dostaję 0 za każdym razem w moim ubuntu 11.10.

#include <stdio.h>
#include <stdlib.h>

int main()
{
    double *a = (double*) malloc(sizeof(double)*100)
    printf("%f", *a);
}

Dlaczego malloc zachowuje się tak, mimo że jest calloc?

Czy to nie znaczy, że istnieje niepożądany narzut wydajności po prostu zainicjować wartości do 0, nawet jeśli nie chcesz, aby to było czasami?


EDIT: Och, mój poprzedni przykład nie był inicjowany, ale zdarzyło się użyć " fresh" blok.

To, czego dokładnie Szukałem, to dlaczego inicjalizuje go, gdy przydziela duży blok:

int main()
{
    int *a = (int*) malloc(sizeof(int)*200000);
    a[10] = 3;
    printf("%d", *(a+10));

    free(a);

    a = (double*) malloc(sizeof(double)*200000);
    printf("%d", *(a+10));
}

OUTPUT: 3
        0 (initialized)

Ale dzięki za wskazanie, że istnieje powód bezpieczeństwa podczas mallocingu! (Nigdy o tym nie myślałem). Na pewno musi inicjalizować do zera przy przydzielaniu świeżego bloku lub dużego bloku.

Author: Jeegar Patel, 2011-11-06

9 answers

Krótka Odpowiedź:

Tak się składa, że w Twoim przypadku jest zero.
(również Twój przypadek testowy nie pokazuje, że dane są zerowe. Pokazuje tylko wtedy, gdy jeden element jest zerowy.)

Długa Odpowiedź:

Kiedy zadzwonisz malloc(), wydarzy się jedna z dwóch rzeczy:

  1. przetwarza pamięć, która została wcześniej przydzielona i uwolniona z tego samego procesu.
  2. żąda nowych stron z systemu operacyjnego.

W pierwszym przypadku, pamięć będzie zawierać dane z poprzednich przydziałów. Więc to nie będzie zero. Jest to typowy przypadek przy wykonywaniu małych przydziałów.

W drugim przypadku, pamięć będzie z systemu operacyjnego. Dzieje się tak, gdy programowi kończy się pamięć - lub gdy żądasz bardzo dużego przydziału. (jak w twoim przykładzie)

Oto haczyk: Pamięć pochodząca z systemu operacyjnego zostanie wyzerowana z powodów bezpieczeństwa.*

Gdy system operacyjny daje Ci pamięć, mogła zostać uwolniona z innego procesu. Aby pamięć mogła zawierać poufne informacje, takie jak hasło. Aby zapobiec odczytywaniu takich danych, SYSTEM OPERACYJNY zeruje je, zanim ci je poda.

*zauważam, że standard C nic na ten temat nie mówi. Jest to zachowanie ściśle OS. Tak więc zerowanie to może, ale nie musi być obecne w systemach, w których bezpieczeństwo nie jest problemem.


Aby dać więcej tła wydajności do tego:

Jako @R. wspominając w komentarzach, to zerowanie jest powodem, dla którego zawsze należy używać calloc() zamiast malloc() + memset(). calloc() może wykorzystać ten fakt, aby uniknąć oddzielnego memset().


Z drugiej strony to zerowanie jest czasem wąskim gardłem wydajności. W niektórych aplikacjach numerycznych (takich jak out-of-place FFT), trzeba przeznaczyć ogromną część pamięci scratch. Użyj go do wykonania dowolnego algorytmu, a następnie uwolnij go.

w takich przypadkach zerowanie jest niepotrzebne i wynosi do czystej nad głową.

Najbardziej ekstremalnym przykładem, jaki widziałem, jest 20-sekundowe zerowanie narzutu na 70-sekundową operację z buforem scratch 48 GB. (Około 30% kosztów.) [[39]}(przyznane: maszyna nie miała przepustowości PAMIĘCI.)

Oczywistym rozwiązaniem jest po prostu ponowne użycie pamięci ręcznie. Ale to często wymaga przełamania ustalonych interfejsów. (zwłaszcza jeśli jest to część procedury bibliotecznej)

 163
Author: Mysticial,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:25

SYSTEM OPERACYJNY Zwykle wyczyści nowe strony pamięci, które wysyła do twojego procesu, więc nie może patrzeć na dane starszego procesu. Oznacza to, że po raz pierwszy zainicjalizujesz zmienną (lub coś malloc), często będzie to zero, ale jeśli kiedykolwiek użyjesz tej pamięci ponownie (na przykład uwalniając ją i malloc-ing ponownie), wtedy wszystkie zakłady są wyłączone.

Ta nieścisłość jest właśnie powodem, dla którego niezainicjowane zmienne są tak trudnym do znalezienia błędem.


Co do niechcianych kosztów ogólnych, unikanie nieokreślonego zachowania jest prawdopodobnie ważniejsze . Jakikolwiek mały wzrost wydajności, jaki można uzyskać w tym przypadku, nie zrekompensuje trudnego do znalezienia błędów, z którymi będziesz musiał sobie poradzić, jeśli ktoś nieznacznie zmodyfikuje kody (łamiąc poprzednie założenia) lub przeniesie je do innego systemu (gdzie założenia mogły być nieważne w pierwszej kolejności).

 20
Author: hugomg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-06 19:10:36

Dlaczego zakładasz, że malloc() inicjalizuje się na zero? Tak się składa, że pierwsze wywołanie malloc() powoduje wywołanie sbrk lub mmap wywołań systemowych, które przydzielają stronę pamięci z systemu operacyjnego. System Operacyjny jest zobowiązany do zapewnienia pamięci zerowej ze względów bezpieczeństwa (w przeciwnym razie dane z innych procesów będą widoczne!). Więc możesz pomyśleć - system operacyjny marnuje czas na zerowanie strony. Ale nie! W Linuksie istnieje specjalna systemowa strona singletonowa o nazwie 'strona zerowa' i że strona zostanie zmapowana jako Copy-On-Write, co oznacza, że tylko wtedy, gdy rzeczywiście piszesz na tej stronie, SYSTEM OPERACYJNY przydzieli inną stronę i zainicjalizuje ją. Mam więc nadzieję, że to odpowie na twoje pytanie dotyczące wydajności. Model stronicowania pamięci pozwala na leniwe korzystanie z pamięci, wspierając możliwość wielokrotnego mapowania tej samej strony oraz możliwość obsługi przypadku, gdy wystąpi pierwszy zapis.

Jeśli wywołasz free(), alokator glibc zwróci region do jego wolnego listy, a gdy malloc() zostanie wywołane ponownie, możesz uzyskać ten sam region, ale brudny z poprzednimi danymi. W końcu free() może przywrócić pamięć do systemu operacyjnego, wywołując ponownie wywołania systemowe.

Zauważ, że glibc strona man na malloc() ściśle mówi, że pamięć nie jest czyszczona, więc przez "kontrakt" na API nie można zakładać, że zostanie wyczyszczona. Oto oryginalny fragment:

Malloc() przydziela rozmiar bajtów i zwraca wskaźnik do przydzielonego pamięć.
Pamięć nie jest wyczyszczona. Jeśli size jest równe 0, to malloc() zwraca NULL, lub unikalną wartość wskaźnika, która może być później pomyślnie przekazana do funkcji free ().

Jeśli chcesz, możesz przeczytać więcej o tej dokumentacji, jeśli martwisz się o wydajność lub inne skutki uboczne.

 15
Author: Dan Aloni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-06 19:32:17

Zmodyfikowałem twój przykład, aby zawierał 2 identyczne przydziały. Teraz łatwo zauważyć, że malloc nie zeruje inicjalizacji pamięci.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    {
      double *a = malloc(sizeof(double)*100);
      *a = 100;
      printf("%f\n", *a);
      free(a);
    }
    {
      double *a = malloc(sizeof(double)*100);
      printf("%f\n", *a);
      free(a);
    }

    return 0;
}

Wyjście z gcc 4.3.4

100.000000
100.000000
 13
Author: Praetorian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-06 19:19:24

Standard nie nakazuje malloc() inicjalizacji wartości na zero. Po prostu zdarza się na twojej platformie, że może być ustawiona na zero lub mogło być to zero w określonym momencie odczytu tej wartości.

 2
Author: Muggen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-06 19:09:28

Twój kod nie pokazuje, że malloc inicjalizuje jego pamięć do 0. To może być zrobione przez system operacyjny, przed uruchomieniem programu. Aby zobaczyć przypadek shich, napisz inną wartość do pamięci, zwolnij ją i ponownie zadzwoń do malloc. Prawdopodobnie dostaniesz ten sam adres, ale musisz to sprawdzić. Jeśli tak, możesz sprawdzić, co zawiera. Daj nam znać!

 2
Author: TonyK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-06 19:09:50

Z gnu.org :

Bardzo duże bloki (znacznie większe niż Strona) są przydzielane przez tę implementację za pomocą mmap (anonymous lub poprzez /dev/zero).

 2
Author: TomaszK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-06 19:15:56

Czy wiesz, że na pewno jest inicjowana? Czy jest możliwe, że obszar zwracany przez malloc() po prostu często ma 0 na początku?

 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-06 19:09:26

Nigdy Nigdy nie licz na jakikolwiek kompilator do generowania kodu, który zainicjalizuje pamięć do czegokolwiek. malloc po prostu zwraca wskaźnik do n bajtów pamięci gdzieś może nawet być w swapie.

Jeśli zawartość pamięci jest krytyczna, zainicjuj ją samodzielnie.

 0
Author: FlyingGuy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-06 19:15:36