Поведение неинициализированной переменной в C ++

Себя проверил, такую ​​прогу написал

int main() {
 int i;
 cout << i;
 return 0;
}

Я запускал программу несколько раз, и результат был все время один и тот же, ноль. Я пробовал это на C, и результат был таким же.

Но в моем учебнике сказано

Если вы не инициализируете переменную, определенную внутри функции, значение переменной останется неопределенным. Это означает, что элемент принимает то значение, которое ранее находилось в этом месте в памяти.

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


person omidh    schedule 11.05.2015    source источник
comment
Это неопределенное поведение в C ++ и C. Вы сказали C в заголовке, но cout от C ++.   -  person Shafik Yaghmour    schedule 11.05.2015
comment
И поскольку он не определен, компилятор может (но не обязан) сгенерировать программу так, чтобы перед ее использованием присваивался 0.   -  person deviantfan    schedule 11.05.2015
comment
Что это за учебник?   -  person Lightness Races in Orbit    schedule 11.05.2015
comment
Как вам удалось попробовать cout на C?   -  person Deanie    schedule 11.05.2015
comment
@Daenie Я использовал printf, но мне было лень вставлять C-код   -  person omidh    schedule 12.05.2015


Ответы (4)


Как это возможно, если программа всегда присваивает переменной свободное место в памяти? Как это могло быть что-то, а не ноль?

Давайте посмотрим на пример практической реализации.

Скажем, он использует стек для хранения локальных переменных.

void
foo(void)
{
        int foo_var = 42;
}

void
bar(void)
{
        int bar_var;
        printf("%d\n", bar_var);
}

int
main(void)
{
        bar();
        foo();
        bar();
}

Полностью сломанный код выше иллюстрирует эту мысль. После того, как мы вызываем foo, определенному месту в стеке, куда был помещен foo_var, устанавливается значение 42. Когда мы вызываем bar, bar_var занимает именно это место. И действительно, выполнение кода приводит к печати 0 и 42, показывая, что на значение bar_var нельзя полагаться, если оно не инициализировано.

Теперь должно быть ясно, что требуется инициализация локальной переменной. Но может ли main быть исключением? Есть ли что-нибудь, что могло бы сыграть со стеком и в результате дать нам ненулевое значение?

да. main - это не первая функция, выполняемая в вашей программе. На самом деле требуется тонны работы, чтобы все настроить. Любая из этих работ могла бы использовать стек и оставить в нем ненулевые числа. Мало того, что вы не можете ожидать одинакового значения в разных операционных системах, оно вполне может внезапно измениться в той самой системе, которую вы используете сейчас. Заинтересованные стороны могут поискать в Google "динамический компоновщик".

Наконец, в стандарте языка C нет даже термина «стек». «Место» для локальных переменных остается за компилятором. Он мог даже получить случайную чушь из того, что попало в данный регистр. Это действительно может быть полностью что угодно. Фактически, если запускается неопределенное поведение, компилятор может делать все, что ему хочется.

person employee of the month    schedule 11.05.2015
comment
Стандарт языка C C не является C ++ - person Pharap; 22.04.2017
comment
Поскольку этот пример вызывает неопределенное поведение, вы должны указать точные настройки компилятора и компилятора, которые вы использовали для воспроизведения наблюдаемого поведения. - person Thomas Weller; 29.01.2021

Если вы не инициализируете переменную, определенную внутри функции, значение переменной останется неопределенным.

Это правда.

Это означает, что элемент принимает то значение, которое ранее находилось в этом месте в памяти.

Этого бита нет.

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

Теоретически ваш компилятор мог бы фактически присвоить этому целому числу случайное начальное значение, если бы он захотел, поэтому попытки рационализировать это совершенно бессмысленно. Но давайте продолжим, как если бы мы предположили, что «элемент принимает то значение, которое ранее находилось в этом месте в памяти».

Как это могло быть что-то, а не ноль (я предполагаю, что значение свободной памяти по умолчанию равно нулю)?

Что ж, вот что происходит, когда вы предполагаете. :)

person Lightness Races in Orbit    schedule 11.05.2015
comment
Кроме того, некоторые компиляторы могут полностью исключить доступ к памяти, и в этом случае вместо этого вы получите доступ к значениям регистров. - person yyny; 06.12.2020

Статические переменные и глобальные переменные инициализируются нулем:

Global:
int a;  //a is initialized as 0

void myfunc(){
   static int x;     // x is also initialized as 0
   printf("%d", x);}

Где нестатические переменные или переменные auto, т.е. локальные переменные неопределенные (неопределенные обычно означают, что они могут делать что угодно. Может быть нулевым, это может быть значение, которое было там это может привести к сбою программы). Их чтение до присвоения значения приводит к неопределенному поведению.

void myfunc2(){
   int x;        // value of x is assigned by compiler it can even be 0
   printf("%d", x);}

В основном это зависит от компилятора, но в большинстве случаев компиляторы заранее принимают значение 0.

person S.I.J    schedule 11.05.2015

Этот код вызывает Undefined Behavior (UB), поскольку переменная используется без инициализации.

Компилятор должен выдать предупреждение, когда используется флаг предупреждения, например -Wall:

warning: 'i' is used uninitialized in this function [-Wuninitialized]
  cout << i;
          ^

Просто так случилось, что при этом прогоне в вашей системе он имел значение 0. Это означает, что значение мусора, которому была назначена переменная, оказалось равным 0, потому что остатки памяти там предполагали это.

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

person gsamaras    schedule 04.07.2018