Der von malloc() zurückgegebene Speicher ist nicht Null initialisiert. Gehen Sie niemals davon aus, dass dies der Fall ist.
In Ihrem Testprogramm ist es nur ein Zufall:Ich tippe auf malloc()
habe gerade einen frischen Block von mmap()
bekommen , aber verlassen Sie sich auch nicht darauf.
Zum Beispiel, wenn ich Ihr Programm auf meiner Maschine so ausführe:
$ echo 'void __attribute__((constructor)) p(void){
void *b = malloc(4444); memset(b, 4, 4444); free(b);
}' | cc -include stdlib.h -include string.h -xc - -shared -o pollute.so
$ LD_PRELOAD=./pollute.so ./your_program
a at 0x7ffd40d3aa60: 1256994848 21891 1256994464 21891 1087613792 32765 0 0
b at 0x55834c75d010: 67372036 67372036 67372036 67372036 67372036 67372036 67372036 67372036
Ihr zweites Beispiel zeigt einfach ein Artefakt der malloc
Implementierung in glibc; Wenn Sie das wiederholen, malloc
/free
Bei einem Puffer größer als 8 Bytes werden Sie deutlich sehen, dass nur die ersten 8 Bytes auf Null gesetzt werden, wie im folgenden Beispielcode.
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
const size_t n = 4;
const size_t m = 0x10;
int main()
{
for (size_t i = n; i; --i) {
int *const p = malloc(m*sizeof(int));
printf("%p ", p);
for (size_t j = 0; j < m; ++j) {
printf("%d:", p[j]);
++p[j];
printf("%d ", p[j]);
}
free(p);
printf("\n");
}
return 0;
}
Ausgabe:
0x55be12864010 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1 0:1
0x55be12864010 0:1 0:1 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2 1:2
0x55be12864010 0:1 0:1 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3 2:3
0x55be12864010 0:1 0:1 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4 3:4
Unabhängig davon, wie der Stack initialisiert wird, sehen Sie keinen ursprünglichen Stack, da die C-Bibliothek eine Reihe von Dingen ausführt, bevor sie main
aufruft , und sie berühren den Stapel.
Bei der GNU-C-Bibliothek beginnt die Ausführung auf x86-64 am Einstiegspunkt _start, der __libc_start_main
aufruft Dinge einzurichten, und letzteres ruft am Ende main
auf . Aber bevor Sie main
aufrufen , ruft es eine Reihe anderer Funktionen auf, wodurch verschiedene Daten auf den Stack geschrieben werden. Der Inhalt des Stapels wird zwischen Funktionsaufrufen nicht gelöscht, also wenn Sie in main
kommen , Ihr Stack enthält Reste der vorherigen Funktionsaufrufe.
Dies erklärt nur die Ergebnisse, die Sie aus dem Stack erhalten, siehe die anderen Antworten zu Ihrem allgemeinen Ansatz und Ihren Annahmen.
In beiden Fällen werden Sie nicht initialisiert Speicher, und Sie können keine Vermutungen über seinen Inhalt anstellen.
Wenn das Betriebssystem Ihrem Prozess eine neue Seite zuweisen muss (sei es für seinen Stack oder für die von malloc()
verwendete Arena). ), es garantiert, dass es keine Daten von anderen Prozessen offenlegt; Der übliche Weg, dies sicherzustellen, besteht darin, es mit Nullen zu füllen (aber es ist genauso gültig, es mit irgendetwas anderem zu überschreiben, einschließlich sogar einer Seite im Wert von /dev/urandom
- Tatsächlich etwas Debugging malloc()
Implementierungen schreiben Nicht-Null-Muster, um falsche Annahmen wie Ihre abzufangen).
Wenn malloc()
kann die Anforderung aus dem Speicher erfüllen, der bereits verwendet und von diesem Prozess freigegeben wurde, wird sein Inhalt nicht gelöscht (tatsächlich hat das Löschen nichts mit malloc()
zu tun und es kann nicht sein - es muss passieren, bevor der Speicher Ihrem Adressraum zugeordnet wird). Möglicherweise erhalten Sie Speicher, der zuvor von Ihrem Prozess/Programm geschrieben wurde (z. B. vor main()
).
In Ihrem Beispielprogramm sehen Sie einen malloc()
Region, die noch nicht von diesem Prozess geschrieben wurde (d. h. sie stammt direkt von einer neuen Seite) und ein Stack, in den geschrieben wurde (von pre-main()
Code in Ihrem Programm). Wenn Sie mehr von dem Stapel untersuchen, werden Sie feststellen, dass er weiter unten (in seiner Wachstumsrichtung) mit Nullen gefüllt ist.
Wenn Sie wirklich verstehen wollen, was auf Betriebssystemebene passiert, empfehle ich Ihnen, die Ebene der C-Bibliothek zu umgehen und mit Systemaufrufen wie brk()
zu interagieren und mmap()
stattdessen.