Гм, разве это не определенная переменная, а не объявленная ? (Прошу прощения, если это мой C ++ сияет ...)
SBI
6
Нет. Я могу объявить переменную, не определяя ее: extern int x;однако определение всегда подразумевает объявление. Это неверно в C ++, со статическими переменными-членами класса можно определить без объявления, так как объявление должно быть в определении класса (а не в объявлении!), А определение должно быть вне определения класса.
Статические переменные (область действия файла и статическая функция) инициализируются нулем:
int x; // zeroint y = 0; // also zerovoidfoo(){
staticint x; // also zero
}
Нестатические переменные (локальные переменные) не определены . Их чтение перед присвоением значения приводит к неопределенному поведению .
voidfoo(){
int x;
printf("%d", x); // the compiler is free to crash here
}
На практике они, как правило, изначально имеют какое-то бессмысленное значение - некоторые компиляторы могут даже вводить определенные фиксированные значения, чтобы сделать это очевидным при просмотре отладчика, - но, строго говоря, компилятор может делать что угодно, от сбоя до вызова демоны через носовые ходы .
Что касается того, почему это поведение undefined, а не просто «неопределенное / произвольное значение», существует ряд архитектур ЦП, которые имеют в своем представлении дополнительные биты флагов для различных типов. Современный пример - Itanium, в регистрах которого есть бит Not a Thing ; конечно, разработчики стандарта C рассматривали некоторые более старые архитектуры.
Попытка работать со значением с установленными этими флаговыми битами может привести к исключению ЦП в операции, которая действительно не должна завершиться неудачей (например, сложение целых чисел или присвоение другой переменной). И если вы оставите переменную неинициализированной, компилятор может собрать какой-то случайный мусор с установленными битами флага - это означает, что прикосновение к этой неинициализированной переменной может быть смертельным.
о нет, они не такие. Они могут быть в режиме отладки, когда вы не на глазах у клиента, в месяцы с R, если вам повезет,
Мартин Беккет,
8
что нет? статическая инициализация требуется стандартом; см. ИСО / МЭК 9899: 1999 6.7.8 # 10
bdonlan
3
Первый пример хорош, насколько я могу судить. Я меньше понимаю, почему компилятор может
6
@Stuart: есть вещь, называемая «представление ловушки», которая в основном представляет собой битовый шаблон, который не обозначает допустимое значение и может вызывать, например, аппаратные исключения во время выполнения. Единственный тип C, для которого есть гарантия, что любой битовый шаблон является допустимым значением, - это char; все остальные могут иметь представления ловушек. В качестве альтернативы - поскольку доступ к неинициализированной переменной в любом случае осуществляется через UB - соответствующий компилятор может просто выполнить некоторую проверку и решить сообщить о проблеме.
Павел Минаев
5
bdonian правильный. C всегда определялся довольно точно. До C89 и C99 в начале 1970-х годов все эти вещи были описаны в одной из статей dmr. Даже в самой грубой встраиваемой системе требуется всего одна функция memset (), чтобы все делать правильно, так что нет оправдания несоответствующей среде. В своем ответе я привел стандарт.
DigitalRoss,
57
0, если статический или глобальный, неопределенный, если класс хранения автоматический
C всегда очень точно определял начальные значения объектов. Если глобальные или static, они будут обнулены. Если auto, значение неопределенное .
Так было в компиляторах до C89 и было указано K&R и в исходном отчете DMR на C.
Так было в C89, см. Раздел 6.5.7 Инициализация .
Если объект, у которого есть автоматическая продолжительность хранения, не инициализирован явно, его значение не определено. Если объект со статической продолжительностью хранения не инициализируется явно, он инициализируется неявно, как если бы каждому члену с арифметическим типом был присвоен 0, а каждому члену с типом указателя была назначена константа нулевого указателя.
Так было в C99, см. Раздел 6.7.8 Инициализация .
Если объект, который имеет автоматическую продолжительность хранения, не инициализирован явно, его значение не определено. Если объект со статической продолжительностью хранения не инициализирован явно, то: - если он имеет тип указателя, он инициализируется нулевым указателем; - если он имеет арифметический тип, он инициализируется (положительным или беззнаковым) нулем; - если это агрегат, каждый член инициализируется (рекурсивно) в соответствии с этими правилами; - если это объединение, первый указанный член инициализируется (рекурсивно) в соответствии с этими правилами.
Что касается того, что именно означает неопределенное , я не уверен для C89, C99 говорит:
3.17.2 неопределенное значение : неопределенное значение или представление прерывания.
Но независимо от того, что говорят стандарты, в реальной жизни каждая страница стека фактически начинается с нуля, но когда ваша программа просматривает любые autoзначения классов хранения, она видит все, что было оставлено вашей собственной программой, когда она в последний раз использовала эти адреса стека. Если вы выделите много autoмассивов, вы увидите, что они в конечном итоге начинаются аккуратно с нуля.
неопределенный обычно (раньше?) означает, что он может все. Это может быть ноль, это может быть значение, которое было там, это может вызвать сбой программы, это может заставить компьютер производить блины с черникой из слота для компакт-диска. у вас нет абсолютно никаких гарантий. Это может вызвать разрушение планеты. По крайней мере, в том, что касается спецификации ... любой, кто создал компилятор, который действительно делал что-либо подобное, был бы очень недоволен B-)
Брайан Постоу
В проекте C11 N1570 определение indeterminate valueможно найти в 3.19.2.
user3528438
Так ли это, что всегда зависит от компилятора или ОС, какое значение он устанавливает для статической переменной? Например, если кто-то напишет мою собственную ОС или компилятор, и если они также установят начальное значение по умолчанию для статики как неопределенное, возможно ли это?
Адитья Сингх
1
@AdityaSingh, ОС может упростить компилятору, но, в конечном итоге, основная ответственность компилятора - запуск существующего в мире каталога кода C и вторичная ответственность - соответствовать стандартам. Конечно, можно было бы поступить иначе, но почему? Кроме того, сложно сделать статические данные неопределенными, потому что ОС действительно захочет сначала обнулить страницы по соображениям безопасности. (Авто переменных только внешне непредсказуемы , потому что ваша собственная программа, как правило , использовали эти адреса стеки на более раннем этапе.)
DigitalRoss
@BrianPostow Нет, это неверно. См. Stackoverflow.com/a/40674888/584518 . Использование неопределенного значения приводит к неопределенному поведению, а не к неопределенному поведению, за исключением случаев представления ловушек.
Lundin
12
Это зависит от продолжительности хранения переменной. Переменная со статической продолжительностью хранения всегда неявно инициализируется нулем.
Что касается автоматических (локальных) переменных, то неинициализированная переменная имеет неопределенное значение . Неопределенное значение, помимо прочего, означает, что любое «значение», которое вы можете «увидеть» в этой переменной, не только непредсказуемо, но даже не гарантирует стабильности . Например, на практике (т.е. игнорирование UB на секунду) этот код
int num;
int a = num;
int b = num;
не гарантирует, что переменные aи bполучат одинаковые значения. Интересно, что это не какая-то педантичная теоретическая концепция, это часто случается на практике как следствие оптимизации.
В общем, популярный ответ, что «он инициализируется тем мусором, который был в памяти», даже отдаленно неверен. Поведение неинициализированной переменной отличается от поведения переменной, инициализированной с помощью мусора.
%rdiэто первый аргумент printf, поэтому строка "%d\n"по адресу0x4005e4
%rsiэто второй аргумент printf, таким образом i.
Это -0x4(%rbp)первая 4-байтовая локальная переменная.
На данный момент rbpядро выделило значение is на первой странице стека, поэтому, чтобы понять это значение, нам нужно заглянуть в код ядра и выяснить, что оно устанавливает.
TODO устанавливает ли ядро этой памяти на что-то, прежде чем повторно использовать ее для других процессов, когда процесс умирает? В противном случае новый процесс сможет читать память других готовых программ, что приводит к утечке данных. См.: Являются ли неинициализированные значения угрозой безопасности?
Затем мы также можем поиграть с нашими собственными модификациями стека и написать такие забавные вещи, как:
#include<assert.h>intf(){
int i = 13;
return i;
}
intg(){
int i;
return i;
}
intmain(){
f();
assert(g() == 13);
}
# 601044 <i>говорит, что iнаходится по адресу 0x601044и:
readelf -SW a.out
содержит:
[25] .bss NOBITS 000000000060104000104000000800 WA 004
который говорит, что 0x601044находится прямо в середине .bssраздела, который начинается с 0x601040и имеет длину 8 байт.
Затем стандарт ELF гарантирует, что указанный раздел .bssполностью заполнен нулями:
.bssВ этом разделе хранятся неинициализированные данные, которые вносят вклад в образ памяти программы. По определению, система инициализирует данные нулями, когда программа начинает работать. Раздел не занимает файлового пространства, на что указывает тип раздела SHT_NOBITS.
Кроме того, тип SHT_NOBITSэффективен и не занимает места в исполняемом файле:
sh_sizeЭтот член указывает размер раздела в байтах. Если тип SHT_NOBITSраздела не задан , он занимает sh_size
байты в файле. Типовой раздел SHT_NOBITSможет иметь ненулевой размер, но он не занимает места в файле.
Затем ядро Linux должно обнулить эту область памяти при загрузке программы в память при ее запуске.
Это зависит от. Если это определение является глобальным (вне какой-либо функции), оно numбудет инициализировано нулем. Если он локальный (внутри функции), то его значение не определено. Теоретически, даже попытка прочитать значение имеет неопределенное поведение - C допускает возможность битов, которые не вносят вклад в значение, но должны быть установлены определенным образом, чтобы вы даже могли получить определенные результаты от чтения переменной.
Поскольку компьютеры имеют ограниченную емкость памяти, автоматические переменные обычно хранятся в элементах памяти (будь то регистры или ОЗУ), которые ранее использовались для некоторых других произвольных целей. Если такая переменная используется до того, как ей было присвоено значение, это хранилище может содержать все, что было ранее, и поэтому содержимое переменной будет непредсказуемым.
В качестве дополнительной проблемы многие компиляторы могут хранить переменные в регистрах, размер которых превышает размер соответствующих типов. Хотя компилятор должен гарантировать, что любое значение, записываемое в переменную и считываемое обратно, будет усечено и / или расширено знаком до своего надлежащего размера, многие компиляторы будут выполнять такое усечение при записи переменных и ожидают, что оно будет иметь было выполнено до чтения переменной. На таких компиляторах что-то вроде:
вполне может привести к wow()сохранению значений 1234567 в регистрах 0 и 1 соответственно и вызову foo(). Поскольку xв "foo" не требуется, и поскольку функции должны помещать свое возвращаемое значение в регистр 0, компилятор может выделить регистр 0 для q. Если mode1 или 3, в регистр 0 будет загружено 2 или 4, соответственно, но если это какое-то другое значение, функция может вернуть все, что было в регистре 0 (т.е. значение 1234567), даже если это значение находится вне диапазона из uint16_t.
Чтобы избежать необходимости от компиляторов выполнять дополнительную работу, чтобы гарантировать, что неинициализированные переменные никогда не будут хранить значения за пределами своего домена, и избежать необходимости указывать неопределенное поведение с чрезмерной детализацией, в Стандарте говорится, что использование неинициализированных автоматических переменных является неопределенным поведением. В некоторых случаях последствия этого могут быть даже более неожиданными, чем выход значения за пределы диапазона своего типа. Например, учитывая:
voidmoo(int mode){
if (mode < 5)
launch_nukes();
hey(0, mode);
}
компилятор может сделать вывод, что, поскольку вызов moo()с режимом, превышающим 3, неизбежно приведет к вызову программы Undefined Behavior, компилятор может пропустить любой код, который будет иметь значение, только если он modeравен 4 или больше, например, код, который обычно предотвращает запуск ядерного оружия в таких случаях. Обратите внимание, что ни Стандарт, ни современная философия компилятора не заботятся о том, что возвращаемое значение из «эй» игнорируется - попытка вернуть его дает компилятору неограниченную лицензию на генерацию произвольного кода.
Если из-за этого вы видите странное поведение, это может зависеть от того, где оно объявлено. Если внутри функции в стеке, то содержимое, скорее всего, будет отличаться каждый раз при вызове функции. Если это статическая или модульная область видимости, она не определена, но не изменится.
Если класс хранения является статическим или глобальным, то во время загрузки BSS инициализирует переменную или ячейку памяти (ML) до 0, если переменной изначально не присвоено какое-либо значение. В случае локальных неинициализированных переменных представление ловушки назначается ячейке памяти. Поэтому, если какой-либо из ваших регистров, содержащих важную информацию, будет перезаписан компилятором, программа может аварийно завершить работу.
но у некоторых компиляторов может быть механизм, позволяющий избежать такой проблемы.
Я работал с серией nec v850, когда понял, что существует представление ловушки, в котором есть битовые шаблоны, которые представляют неопределенные значения для типов данных, кроме char. Когда я взял неинициализированный char, я получил нулевое значение по умолчанию из-за представления ловушки. Это может быть полезно для любого1, использующего necv850es
Ваша система не соответствует требованиям, если вы получаете представление прерывания при использовании unsigned char. Им явно не разрешено содержать представления прерываний, C17 6.2.6.1/5.
Lundin
-2
Значение num будет неким мусорным значением из основной памяти (RAM). лучше, если вы инициализируете переменную сразу после создания.
Насколько я понимаю, это в основном зависит от компилятора, но в большинстве случаев компиляторы заранее принимают значение 0.
Я получил значение мусора в случае VC ++, в то время как TC дал значение 0. Я распечатываю его, как показано ниже.
Если вы получаете детерминированное значение, например, 0ваш компилятор, скорее всего, предпримет дополнительные шаги, чтобы убедиться, что он получает это значение (в любом случае добавляя код для инициализации переменных). Некоторые компиляторы делают это при "отладочной" компиляции, но выбор значения 0для них - плохая идея, поскольку это скроет ошибки в вашем коде (более правильным было бы гарантировать действительно маловероятное число, подобное 0xBAADF00Dили что-то подобное). Я думаю, что большинство компиляторов просто оставит любой мусор, который занимает память, как значение переменной (то есть, как правило, он не собирается как 0).
extern int x;
однако определение всегда подразумевает объявление. Это неверно в C ++, со статическими переменными-членами класса можно определить без объявления, так как объявление должно быть в определении класса (а не в объявлении!), А определение должно быть вне определения класса.Ответы:
Статические переменные (область действия файла и статическая функция) инициализируются нулем:
int x; // zero int y = 0; // also zero void foo() { static int x; // also zero }
Нестатические переменные (локальные переменные) не определены . Их чтение перед присвоением значения приводит к неопределенному поведению .
void foo() { int x; printf("%d", x); // the compiler is free to crash here }
На практике они, как правило, изначально имеют какое-то бессмысленное значение - некоторые компиляторы могут даже вводить определенные фиксированные значения, чтобы сделать это очевидным при просмотре отладчика, - но, строго говоря, компилятор может делать что угодно, от сбоя до вызова демоны через носовые ходы .
Что касается того, почему это поведение undefined, а не просто «неопределенное / произвольное значение», существует ряд архитектур ЦП, которые имеют в своем представлении дополнительные биты флагов для различных типов. Современный пример - Itanium, в регистрах которого есть бит Not a Thing ; конечно, разработчики стандарта C рассматривали некоторые более старые архитектуры.
Попытка работать со значением с установленными этими флаговыми битами может привести к исключению ЦП в операции, которая действительно не должна завершиться неудачей (например, сложение целых чисел или присвоение другой переменной). И если вы оставите переменную неинициализированной, компилятор может собрать какой-то случайный мусор с установленными битами флага - это означает, что прикосновение к этой неинициализированной переменной может быть смертельным.
источник
char
; все остальные могут иметь представления ловушек. В качестве альтернативы - поскольку доступ к неинициализированной переменной в любом случае осуществляется через UB - соответствующий компилятор может просто выполнить некоторую проверку и решить сообщить о проблеме.0, если статический или глобальный, неопределенный, если класс хранения автоматический
C всегда очень точно определял начальные значения объектов. Если глобальные или
static
, они будут обнулены. Еслиauto
, значение неопределенное .Так было в компиляторах до C89 и было указано K&R и в исходном отчете DMR на C.
Так было в C89, см. Раздел 6.5.7 Инициализация .
Так было в C99, см. Раздел 6.7.8 Инициализация .
Что касается того, что именно означает неопределенное , я не уверен для C89, C99 говорит:
Но независимо от того, что говорят стандарты, в реальной жизни каждая страница стека фактически начинается с нуля, но когда ваша программа просматривает любые
auto
значения классов хранения, она видит все, что было оставлено вашей собственной программой, когда она в последний раз использовала эти адреса стека. Если вы выделите многоauto
массивов, вы увидите, что они в конечном итоге начинаются аккуратно с нуля.Вы можете спросить, почему это так? Другой ответ SO касается этого вопроса, см .: https://stackoverflow.com/a/2091505/140740
источник
indeterminate value
можно найти в 3.19.2.Это зависит от продолжительности хранения переменной. Переменная со статической продолжительностью хранения всегда неявно инициализируется нулем.
Что касается автоматических (локальных) переменных, то неинициализированная переменная имеет неопределенное значение . Неопределенное значение, помимо прочего, означает, что любое «значение», которое вы можете «увидеть» в этой переменной, не только непредсказуемо, но даже не гарантирует стабильности . Например, на практике (т.е. игнорирование UB на секунду) этот код
int num; int a = num; int b = num;
не гарантирует, что переменные
a
иb
получат одинаковые значения. Интересно, что это не какая-то педантичная теоретическая концепция, это часто случается на практике как следствие оптимизации.В общем, популярный ответ, что «он инициализируется тем мусором, который был в памяти», даже отдаленно неверен. Поведение неинициализированной переменной отличается от поведения переменной, инициализированной с помощью мусора.
источник
Пример Ubuntu 15.10, Kernel 4.2.0, x86-64, GCC 5.2.1
Достаточно стандартов, давайте посмотрим на реализацию :-)
Локальная переменная
Стандарты: неопределенное поведение.
Реализация: программа выделяет пространство стека и никогда ничего не перемещает по этому адресу, поэтому используется все, что было там ранее.
#include <stdio.h> int main() { int i; printf("%d\n", i); }
компилировать с помощью:
gcc -O0 -std=c99 a.c
выходы:
0
и декомпилируется с помощью:
кому:
0000000000400536 <main>: 400536: 55 push %rbp 400537: 48 89 e5 mov %rsp,%rbp 40053a: 48 83 ec 10 sub $0x10,%rsp 40053e: 8b 45 fc mov -0x4(%rbp),%eax 400541: 89 c6 mov %eax,%esi 400543: bf e4 05 40 00 mov $0x4005e4,%edi 400548: b8 00 00 00 00 mov $0x0,%eax 40054d: e8 be fe ff ff callq 400410 <printf@plt> 400552: b8 00 00 00 00 mov $0x0,%eax 400557: c9 leaveq 400558: c3 retq
Из наших знаний о соглашениях о вызовах x86-64:
%rdi
это первый аргумент printf, поэтому строка"%d\n"
по адресу0x4005e4
%rsi
это второй аргумент printf, таким образомi
.Это
-0x4(%rbp)
первая 4-байтовая локальная переменная.На данный момент
rbp
ядро выделило значение is на первой странице стека, поэтому, чтобы понять это значение, нам нужно заглянуть в код ядра и выяснить, что оно устанавливает.TODO устанавливает ли ядро этой памяти на что-то, прежде чем повторно использовать ее для других процессов, когда процесс умирает? В противном случае новый процесс сможет читать память других готовых программ, что приводит к утечке данных. См.: Являются ли неинициализированные значения угрозой безопасности?
Затем мы также можем поиграть с нашими собственными модификациями стека и написать такие забавные вещи, как:
#include <assert.h> int f() { int i = 13; return i; } int g() { int i; return i; } int main() { f(); assert(g() == 13); }
Локальная переменная в
-O3
Анализ реализации на: Что означает <value optimized out> в gdb?
Глобальные переменные
Стандарты: 0
Реализация:
.bss
раздел.#include <stdio.h> int i; int main() { printf("%d\n", i); } gcc -00 -std=c99 a.c
компилируется в:
0000000000400536 <main>: 400536: 55 push %rbp 400537: 48 89 e5 mov %rsp,%rbp 40053a: 8b 05 04 0b 20 00 mov 0x200b04(%rip),%eax # 601044 <i> 400540: 89 c6 mov %eax,%esi 400542: bf e4 05 40 00 mov $0x4005e4,%edi 400547: b8 00 00 00 00 mov $0x0,%eax 40054c: e8 bf fe ff ff callq 400410 <printf@plt> 400551: b8 00 00 00 00 mov $0x0,%eax 400556: 5d pop %rbp 400557: c3 retq 400558: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1) 40055f: 00
# 601044 <i>
говорит, чтоi
находится по адресу0x601044
и:содержит:
[25] .bss NOBITS 0000000000601040 001040 000008 00 WA 0 0 4
который говорит, что
0x601044
находится прямо в середине.bss
раздела, который начинается с0x601040
и имеет длину 8 байт.Затем стандарт ELF гарантирует, что указанный раздел
.bss
полностью заполнен нулями:Кроме того, тип
SHT_NOBITS
эффективен и не занимает места в исполняемом файле:Затем ядро Linux должно обнулить эту область памяти при загрузке программы в память при ее запуске.
источник
Это зависит от. Если это определение является глобальным (вне какой-либо функции), оно
num
будет инициализировано нулем. Если он локальный (внутри функции), то его значение не определено. Теоретически, даже попытка прочитать значение имеет неопределенное поведение - C допускает возможность битов, которые не вносят вклад в значение, но должны быть установлены определенным образом, чтобы вы даже могли получить определенные результаты от чтения переменной.источник
Поскольку компьютеры имеют ограниченную емкость памяти, автоматические переменные обычно хранятся в элементах памяти (будь то регистры или ОЗУ), которые ранее использовались для некоторых других произвольных целей. Если такая переменная используется до того, как ей было присвоено значение, это хранилище может содержать все, что было ранее, и поэтому содержимое переменной будет непредсказуемым.
В качестве дополнительной проблемы многие компиляторы могут хранить переменные в регистрах, размер которых превышает размер соответствующих типов. Хотя компилятор должен гарантировать, что любое значение, записываемое в переменную и считываемое обратно, будет усечено и / или расширено знаком до своего надлежащего размера, многие компиляторы будут выполнять такое усечение при записи переменных и ожидают, что оно будет иметь было выполнено до чтения переменной. На таких компиляторах что-то вроде:
uint16_t hey(uint32_t x, uint32_t mode) { uint16_t q; if (mode==1) q=2; if (mode==3) q=4; return q; } uint32_t wow(uint32_t mode) { return hey(1234567, mode); }
вполне может привести к
wow()
сохранению значений 1234567 в регистрах 0 и 1 соответственно и вызовуfoo()
. Посколькуx
в "foo" не требуется, и поскольку функции должны помещать свое возвращаемое значение в регистр 0, компилятор может выделить регистр 0 дляq
. Еслиmode
1 или 3, в регистр 0 будет загружено 2 или 4, соответственно, но если это какое-то другое значение, функция может вернуть все, что было в регистре 0 (т.е. значение 1234567), даже если это значение находится вне диапазона из uint16_t.Чтобы избежать необходимости от компиляторов выполнять дополнительную работу, чтобы гарантировать, что неинициализированные переменные никогда не будут хранить значения за пределами своего домена, и избежать необходимости указывать неопределенное поведение с чрезмерной детализацией, в Стандарте говорится, что использование неинициализированных автоматических переменных является неопределенным поведением. В некоторых случаях последствия этого могут быть даже более неожиданными, чем выход значения за пределы диапазона своего типа. Например, учитывая:
void moo(int mode) { if (mode < 5) launch_nukes(); hey(0, mode); }
компилятор может сделать вывод, что, поскольку вызов
moo()
с режимом, превышающим 3, неизбежно приведет к вызову программы Undefined Behavior, компилятор может пропустить любой код, который будет иметь значение, только если онmode
равен 4 или больше, например, код, который обычно предотвращает запуск ядерного оружия в таких случаях. Обратите внимание, что ни Стандарт, ни современная философия компилятора не заботятся о том, что возвращаемое значение из «эй» игнорируется - попытка вернуть его дает компилятору неограниченную лицензию на генерацию произвольного кода.источник
Основной ответ - да, это не определено.
Если из-за этого вы видите странное поведение, это может зависеть от того, где оно объявлено. Если внутри функции в стеке, то содержимое, скорее всего, будет отличаться каждый раз при вызове функции. Если это статическая или модульная область видимости, она не определена, но не изменится.
источник
Если класс хранения является статическим или глобальным, то во время загрузки BSS инициализирует переменную или ячейку памяти (ML) до 0, если переменной изначально не присвоено какое-либо значение. В случае локальных неинициализированных переменных представление ловушки назначается ячейке памяти. Поэтому, если какой-либо из ваших регистров, содержащих важную информацию, будет перезаписан компилятором, программа может аварийно завершить работу.
но у некоторых компиляторов может быть механизм, позволяющий избежать такой проблемы.
Я работал с серией nec v850, когда понял, что существует представление ловушки, в котором есть битовые шаблоны, которые представляют неопределенные значения для типов данных, кроме char. Когда я взял неинициализированный char, я получил нулевое значение по умолчанию из-за представления ловушки. Это может быть полезно для любого1, использующего necv850es
источник
Значение num будет неким мусорным значением из основной памяти (RAM). лучше, если вы инициализируете переменную сразу после создания.
источник
Насколько я понимаю, это в основном зависит от компилятора, но в большинстве случаев компиляторы заранее принимают значение 0.
Я получил значение мусора в случае VC ++, в то время как TC дал значение 0. Я распечатываю его, как показано ниже.
int i; printf('%d',i);
источник
0
ваш компилятор, скорее всего, предпримет дополнительные шаги, чтобы убедиться, что он получает это значение (в любом случае добавляя код для инициализации переменных). Некоторые компиляторы делают это при "отладочной" компиляции, но выбор значения0
для них - плохая идея, поскольку это скроет ошибки в вашем коде (более правильным было бы гарантировать действительно маловероятное число, подобное0xBAADF00D
или что-то подобное). Я думаю, что большинство компиляторов просто оставит любой мусор, который занимает память, как значение переменной (то есть, как правило, он не собирается как0
).