Неопределенное поведение означает, что может произойти все, в том числе и то, что вы ожидаете. В этом случае ваши переменные стека не были перезаписаны.
void func3() {
int a=0, b=1, c=2;
}
Если вы включите вызов func3()
между func1
и printf
, вы получите другой результат.
РЕДАКТИРОВАТЬ: Что на самом деле происходит на некоторых платформах.
int *func(void)
{
int num;
num = 100;
return #
}
Предположим, для простоты, что указатель стека равен 10, прежде чем вы вызовете эту функцию, и что стек растет вверх.
Когда вы вызываете функцию, адрес возврата помещается в стек (в позицию 10), а указатель стека увеличивается до 14 (да, очень упрощенно). Затем переменная num создается в стеке в позиции 14, а указатель стека увеличивается до 18.
Когда вы возвращаетесь, вы возвращаете указатель на адрес 14 - адрес возврата извлекается из стека, а указатель стека возвращается к 10.
void func2() {
int y = 1;
}
Здесь происходит то же самое. Возвращаемый адрес помещается в позицию, y создается в позиции 14, вы присваиваете 1 для y (записывает в адрес 14), вы возвращаетесь и складываете указатель обратно в позицию 10.
Теперь ваш старый int *
возвратился из func
точек на адрес 14, и последним изменением этого адреса было присвоение локальной переменной func2. Таким образом, у вас есть свисающий указатель (ничто над положением 10 в стеке не является действительным), который указывает на оставшееся значение от вызова к func2