Неинициализированная переменная в C

Я немного смущен. Насколько я знаю, если вы объявите int в C, не инициализируя его, например: int x;

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

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

 int main(){ int a; printf("%d\n",a); return 0; } 

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

 void foo(int *var_handle){ // do nothing } int main(){ int a; foo(&a); printf("%d\n",a); // works, prints some big value return 0; } 

Можете ли вы объяснить поведение этого? мы только добавили вызов функции, которая ничего не делает, но теперь программа не сработает.

Чтение значения неинициализированной переменной приводит к неопределенному поведению. И неопределенное поведение означает, что оно может потерпеть крах. Это не значит, что это произойдет или оно обязательно потерпит крах.

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

Использование неинициализированного значения напрямую не вызывает неопределенное поведение.

Per C 2011 (проект n1570) 6.7.9 10, неинициализированный объект с автоматической продолжительностью хранения имеет неопределенное значение. На 3.19.2 1 неопределенное значение представляет собой либо неопределенное значение, либо ловушечное представление. Аналогичный текст появляется в C 1999 году.

Если объект имеет представление ловушки, может возникнуть неопределенное поведение. Однако, если объект имеет неопределенное значение, тогда программа должна вести себя, если объект имеет определенное детерминированное значение; это просто не указано, какое значение имеет объект. Программе не разрешается сбой только потому, что значение не указано.

Удивительно, что вы сообщаете о простой сбое сбоев программы в Visual Studio 2010, потому что я не ожидаю, что тип int имеет какие-либо ловушечные представления в Visual Studio 2010. Возможно, какой-то исходный файл, отличный от ожидаемого, был скомпилирован и разбит или что вы включили специальные функции отладки в Visual Studio 2010, которые пытаются отслеживать неинициализированные объекты (но не во втором случае, когда вы используете foo ).

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

Конечно, Visual Studio 2010 не соответствует стандарту C, даже не старому стандарту 1999 года, поэтому он не обязан подчиняться вышеуказанным статьям. По сути, все о Visual Studio 2010 – это неопределенное поведение в отношении стандарта C.

Это неопределенное поведение , а это значит, что все может случиться. Буквально ничего. Поведение просто не определено.

Вы можете попробовать это. Я не знаю, является ли это строго неопределенным поведением или нет, но я не могу придумать, как компилятор может вести себя не определенно и по-прежнему соответствовать стандарту C, по крайней мере, если foo находится в разных единицах компиляции ( ~ исходный файл), потому что тогда компилятор не знает, что ему разрешено создавать неопределенное поведение;).

 void foo(int *var_handle){ // do something to var_handle, or maybe nothing, who knows } int main(){ int a[1]; foo(a); printf("%d\n", a[0]); return 0; } 

Изменить: Дальнейшие мысли:

Я вполне уверен, что использовать функцию для инициализации неинициализированной локальной переменной можно, указав неконстантный указатель на локальную переменную функции. Таким образом, просто получение адреса локальной переменной делает его определяемой переменной с неопределенным значением в отношении компилятора. Компилятор не может знать, действительно ли функция устанавливает значение или нет (функция может быть в библиотеке).

Но это просто объясняет, почему это позволяет избежать краха. По-прежнему может быть, что если функция разрешена быть встроенной и ничего не делает, оптимизатору будет разрешено удалить вызов, а затем также удалить адрес неинициализированной локальной переменной, оставив его по-прежнему в состоянии «неопределенного поведения». Вы можете протестировать это для своего компилятора , включив оптимизацию и проверку результатов сборки, чтобы вызвать foo введя inlined (не создавая никакого кода), и посмотрим, сработает ли тогда printf.

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