У моїй системі Debian GNU / Linux 9, коли виконується бінарний файл,
- стек неініціалізований, але
- купа ініціалізується нулем.
Чому?
Я припускаю, що нульова ініціалізація сприяє безпеці, але, якщо для купи, то чому б і не для стека? Чи стек теж не потребує безпеки?
Наскільки я знаю, моє питання не стосується Debian.
Зразок коду С:
#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
Звичайно, стандарт C не вимагає malloc()
очищення пам'яті, перш ніж розподіляти її, але моя програма C лише для ілюстрації. Питання - це не питання про С або про стандартну бібліотеку С. Скоріше, питання - це питання про те, чому ядро та / або завантажувач, який працює під час запуску, нулюють купу, а не стек.
ІНШИЙ ДОСВІД
Моє запитання стосується спостережуваної поведінки GNU / Linux, а не вимог стандартних документів. Якщо ви не впевнені, що я маю на увазі, спробуйте цей код, який викликає подальше невизначене поведінку ( невизначене, тобто, що стосується стандарту С), щоб проілюструвати точку:
#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
Що стосується стандарту С, то поведінка не визначена, тому моє питання не стосується стандарту С. Заклик не malloc()
потрібно повертати одну і ту ж адресу кожен раз, але, оскільки цей дзвінок malloc()
дійсно трапляється щоразу повертати одну і ту ж адресу, цікаво помітити, що пам'ять, що знаходиться в купі, щоразу нулюється.
Стек, навпаки, не здавався нульовим.
Я не знаю, що останній код буде робити на вашій машині, оскільки я не знаю, який шар системи GNU / Linux викликає спостережувану поведінку. Можна, але спробуйте.
ОНОВЛЕННЯ
@Kusalananda помітив у коментарях:
Оскільки це варте, ваш останній код повертає різні адреси та (випадкові) неініціалізовані (ненульові) дані під час запуску на OpenBSD. Це, очевидно, нічого не говорить про поведінку, яку ви спостерігаєте в Linux.
Те, що мій результат відрізняється від результату на OpenBSD, справді цікаво. Мабуть, мої експерименти виявили не протокол безпеки ядра (або лінкера), як я думав, а лише артефакт впровадження.
У цьому світлі я вважаю, що разом відповіді нижче на @mosvy, @StephenKitt та @AndreasGrapentin вирішують моє питання.
Дивіться також про переповнення стека: Чому malloc ініціалізує значення 0 у gcc? (кредит: @bta).
new
оператор в C ++ (також «купа») на Linux просто обгортка для Танос (); ядро не знає і не хвилює, що таке "купа".