В моей системе Debian GNU / Linux 9, когда исполняется двоичный файл,
- стек неинициализирован, но
- куча инициализируется нулями.
Почему?
Я предполагаю, что нулевая инициализация способствует безопасности, но если для кучи, то почему не для стека? Стек тоже не нуждается в безопасности?
Насколько я знаю, мой вопрос не является специфичным для Debian.
Пример кода C:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
const size_t n = 8;
// --------------------------------------------------------------------
// UNINTERESTING CODE
// --------------------------------------------------------------------
static void print_array(
const int *const p, const size_t size, const char *const name
)
{
printf("%s at %p: ", name, p);
for (size_t i = 0; i < size; ++i) printf("%d ", p[i]);
printf("\n");
}
// --------------------------------------------------------------------
// INTERESTING CODE
// --------------------------------------------------------------------
int main()
{
int a[n];
int *const b = malloc(n*sizeof(int));
print_array(a, n, "a");
print_array(b, n, "b");
free(b);
return 0;
}
Выход:
a at 0x7ffe118997e0: 194 0 294230047 32766 294230046 32766 -550453275 32713
b at 0x561d4bbfe010: 0 0 0 0 0 0 0 0
malloc()
Конечно, стандарт C не требует очистки памяти перед ее выделением, но моя программа на C предназначена только для иллюстрации. Вопрос не в вопросе о Си или о стандартной библиотеке Си. Скорее, вопрос заключается в том, почему ядро и / или загрузчик времени выполнения обнуляют кучу, а не стек.
ДРУГОЙ ЭКСПЕРИМЕНТ
Мой вопрос касается наблюдаемого поведения GNU / Linux, а не требований стандартов. Если вы не уверены, что я имею в виду, попробуйте этот код, который вызывает дальнейшее неопределенное поведение ( неопределенное, то есть, что касается стандарта C), чтобы проиллюстрировать это:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
const size_t n = 4;
int main()
{
for (size_t i = n; i; --i) {
int *const p = malloc(sizeof(int));
printf("%p %d ", p, *p);
++*p;
printf("%d\n", *p);
free(p);
}
return 0;
}
Выход из моей машины:
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
0x555e86696010 0 1
Что касается стандарта C, поведение не определено, поэтому мой вопрос не касается стандарта C. Вызов malloc()
необязательно возвращать один и тот же адрес каждый раз, но, поскольку этот вызов malloc()
действительно каждый раз возвращает один и тот же адрес, интересно отметить, что память, находящаяся в куче, каждый раз обнуляется.
Стек, напротив, не был обнулен.
Я не знаю, что будет делать последний код на вашей машине, поскольку я не знаю, какой уровень системы GNU / Linux вызывает наблюдаемое поведение. Вы можете попробовать это.
ОБНОВИТЬ
@Kusalananda заметил в комментариях:
Для чего он стоит, ваш последний код возвращает разные адреса и (иногда) неинициализированные (ненулевые) данные при запуске на OpenBSD. Это, очевидно, ничего не говорит о поведении, которое вы наблюдаете в Linux.
То, что мой результат отличается от результата на OpenBSD, действительно интересно. Судя по всему, в ходе моих экспериментов я обнаружил не протокол безопасности ядра (или компоновщика), как я думал, а простой артефакт реализации.
В этом свете я считаю, что вместе приведенные ниже ответы @mosvy, @StephenKitt и @AndreasGrapentin решают мой вопрос.
См. Также Переполнение стека: почему malloc инициализирует значения в 0 в gcc? (кредит: @bta).
new
оператор в C ++ (также «куча») на Linux просто обертка для таНоса (); ядро не знает и не заботится о том, что такое «куча».