Почему я не получаю 0, 1, 2 в качестве вывода? - PullRequest
1 голос
/ 27 октября 2011

Я сделал небольшой тест, чтобы увидеть, как стек используется в приложении на Си.

#include <stdio.h>

void a(void)
{
    int a = 0;
}

void b(void)
{
    int b;
    printf("%i\n", b++);
}

int main(void)
{
    a();
    b();
    b();
    b();
    fflush(stdin), getc(stdin);
    return 0;
}

Разве b не расположен в том же месте в стеке, где был a? Я ожидаю, что результат будет 0 1 2, но вместо этого я получу одно и то же значение мусора три раза. Почему это так?

Ответы [ 6 ]

10 голосов
/ 27 октября 2011

О единственном способе получить окончательный ответ о том, почему вы получаете то, что вы получаете, это получить вывод на ассемблере от компилятора и посмотреть, что он делает. Предполагается, что весь a() удаляется как мертвый код, а b, вероятно, выделяется в регистре, поэтому, даже если a был выделен и инициализирован, все равно существует реальная вероятность того, что b не окажется в том же хранилище.

С точки зрения языка, на самом деле нет никакого ответа - ваш код просто имеет неопределенное поведение при использовании неинициализированной переменной. Просто чтобы добавить оскорбление к травме, ваш fflush(stdin) также вызывает неопределенное поведение, так что даже если бы остальная часть кода имела какой-то смысл, у вас все равно не было бы никакой гарантии относительно того, какой вывод он выдаст.

3 голосов
/ 27 октября 2011

Я мог бы догадаться, что вы получили все нули в качестве выходных данных, но это не обязательно так.В функции b вы объявляете новый int b, который создается для каждого выполнения кода.B неинициализирован в вашем коде, но некоторые компиляторы обнуляют это значение.Это не является стандартным и НЕ ДОЛЖНО рассчитывать.Вы всегда должны инициализировать свои переменные.

Что касается стека, то это зависит от реализации и зависит от компилятора, настроек оптимизатора и т. Д. Нет гарантии, что эта переменная когда-либо будет существовать в стеке.Скорее всего, это не так, учитывая небольшую продолжительность области действия, которую он может просто хранить в регистре процессора.

В приведенном выше коде b и a являются полностью независимыми переменными, и поэтому не следует рассчитывать на то, чтозначение, даже если они хранятся в одной и той же ячейке памяти.

3 голосов
/ 27 октября 2011

То, что вы делаете, вызывает неопределенное поведение, кроме C99, где значение не определено В любом случае, вы точно не знаете, что произойдет.

Нет гарантий ни состояния стека при выходе из метода, ни значения неинициализированных переменных.

2 голосов
/ 27 октября 2011

Назначение, которое является частью b++ в вашей функции b(), не обязательно должно выполняться компилятором, поскольку b впоследствии не читается.Но что более важно здесь, если у вас нет инициализатора:

The initial value of the object is indeterminate.

вот и все.(Не UB, как говорят другие.) Компилятор может реализовать это любым удобным для него способом.

NB: Слово "стек" нигде не встречается в Cстандарт.Принимая во внимание, что это удобная концепция для реализации auto переменных в C, компилятор не обязан использовать эту концепцию для заданной переменной, и, в частности, вообще нет обязанности хранить переменную в памяти.Он может просто хранить все переменные в регистрах, если платформа позволяет это.Поэтому, если вы посмотрите на ассемблер, созданный для a(), вы, скорее всего, просто увидите ничего , просто пустой возврат.

1 голос
/ 27 октября 2011
Значение

B равно неопределено .Вы не можете ничему научиться, запустив эту программу.

1 голос
/ 27 октября 2011

Вы уже прочитали эти слайды?

http://www.slideshare.net/olvemaudal/deep-c

Существует некоторое обсуждение поведения стека, подобное этому.Конечно, вы никогда не можете полагаться на значение в автоматической переменной.Компилятор может свободно помещать эти переменные в регистры.Или в стеке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...