Неинициализированная переменная int в C - почему это работает? - PullRequest
0 голосов
/ 17 февраля 2019

Я написал программу на C, в которой случайно поместил неинициализированную переменную int.Этот int написан внутри функции, но каким-то образом, когда я печатаю его после приращения, он работает просто отлично.Почему это?Разве это не должно быть неопределенным поведением, печатая буквально мусор?

int n;
int counter;

for (counter = 0; limit > counter; counter++) {
      ++n;
      printf("%d\n", n);
} 

Ответы [ 3 ]

0 голосов
/ 17 февраля 2019

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

Это не означает, что на него можно положиться.Вы все равно должны избегать UB просто потому, что он может плохо себя вести часто странными способами.Фактически, ваш код вполне может печатать вменяемое значение, в то время как также изменяет в произвольном порядке случайные электронные таблицы Excel на вашем столе: -)


(a) См. C11 6.7.9 Initialization /10:

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

И C11 J.2 Undefined behaviour:

Значение объекта с автоматической продолжительностью хранения используется, пока оно не определено.

0 голосов
/ 18 февраля 2019

Когда был написан Стандарт, большинство форм «неопределенного поведения» признавали тот факт, что реализации, предназначенные для разных платформ и целей, будут вести себя по-разному - некоторые полезны и предсказуемы, а другие нет, и признавали, что «рынок»[как это описывается в документе C Rationale] было бы лучше, чем Комитету, судить, какие реализации следует ожидать в каких моделях.

Исторически сложилось, что на большинстве платформ компиляторам ничего не стоило бы вести себя какхотя автоматические объекты были инициализированы каким-либо произвольным образом, который был бы слишком непротиворечивым, чтобы быть полезным для любого вида генератора случайных чисел, но недостаточно надежно предсказуемым, чтобы использовать его для любых других целей , за исключением случаев, когда любое возможное значение было быхорош как любой другой [например, потому что часто легче скопировать объект без учета того, имеет ли он полезное значение, может быть дешевле, чем избегать копирования его, если он не содержит его].На некоторых платформах, однако, единственный способ обеспечить компилятору такое поведение - это явная инициализация таких объектов сама.Авторы Стандарта не хотели требовать, чтобы компиляторы для таких платформ инициализировали объекты с фиктивными значениями, которые, вероятно, были бы перезаписаны программистом, и решили вместо этого потребовать от программистов , чей код должен быть совместим с такими платформами должен был бы гарантировать, что ничто не использовалось без инициализации.

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

void test(int x)
{
  int y,z;
  if (x == 23)
    y=z;
  printf("%d\n",x);
}

и сделать вывод, что было бы «невозможно» вызвать функцию с любым значением x, отличным от 23, и, следовательно, printfследует заменить на puts("23");.Я не думаю, что какие-либо компиляторы вполне настолько агрессивны , но все же , но кажется модным рассматривать генерацию кода, который мог бы выводить другие значения x, как "пропущенные"оптимизация».

0 голосов
/ 17 февраля 2019

Это неопределенное поведение для доступа к значению неинициализированной переменной.Большинство компиляторов по крайней мере предупреждают об этом, и MSVC 2017 по умолчанию будет воспринимать это как ошибку.

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

...