2014-01-16 3 views
4

Я думал, что локальная переменная в C не инициализирована. Но когда я скомпилировал этот код с gcc.Локальная переменная, инициализированная до нуля в C

void f() { 
    static int s; 
    int n; 

    printf("static s = %d\n", s++); 
    printf("local n = %d\n", n++); 

    f(); 
} 

main() { 
    f(); 
} 

И запустить этот код, частичный результат:

static s = 0 
local n = 0 
static s = 1 
local n = 0 
static s = 2 
local n = 0 
static s = 3 
local n = 0 
static s = 4 
local n = 0 
static s = 5 
local n = 0 
... 
static s = 261974 
local n = 0 
static s = 261975 
local n = 0 
static s = 261976 
local n = 0 
static s = 261977 
local n = 0 
static s = 261978 
local n = 0 
static s = 261979 
local n = 0 
static s = 261980 
local n = 0 
static s = 261981 
local n = 0 
Segmentation fault: 11 

Может кто-нибудь, пожалуйста, объясните это? Или обратитесь к стандартной ссылке, что C не будет инициировать локальные вары?

+1

Поведение не определено; все может случиться. – arshajii

+0

@wannik - Удача. Но не берегите его. Это неопределенное поведение. Лучше включите предупреждения компилятора –

+0

Да, undefined. См. Также несколько предыдущих ответов в разделе «Связанные» справа. Серьезно, если вы хотите, чтобы он был инициализирован 0, используйте явное начальное значение. – keshlam

ответ

7

ISO/IEC 9899:TC3 WG14/N1256 (стандарт С99) раздел 6.7.8 пункт 10:

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

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

  • , если он имеет тип указателя, он инициализируется нулевым указателем;
  • если он имеет арифметический тип, он инициализируется (положительным или без знака) нулем;
  • Если это совокупность, каждый член инициализируется (рекурсивно) в соответствии с этими правилами;
  • Если это объединение, первый именованный элемент инициализируется (рекурсивно) в соответствии с этими правилами.

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

+0

Большое вам спасибо, @harmic. Это именно то, что я ищу. http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf – wannik

-1

Переменная должна иметь НЕКОТОРЫЕ значения; спецификация для языка не дает никаких гарантий относительно того, что это будет.

+1

Строго говоря, это не должно иметь никакой ценности. Поведение не определено. –

+0

Есть ли бит-шаблон, который не соответствует значению для int? Неверное поведение не совпадает с неопределенным значением. –

+1

Поведение явно не определено. См. [N1570] (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf): «Если lvalue обозначает объект с автоматическим временем хранения, который мог бы быть объявлен ** класс хранения 'register' ** (никогда не был принят его адрес), и этот объект неинициализирован (не объявлен с инициализатором, а его назначение не было , выполненное до использования), поведение не определено». (Я думаю, что эта формулировка нова в C11.) –

1

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

0

Если переменная инициализируется внутри функции он не инициализируется автоматически. Когда он объявлен вне какой-либо функции, он инициализируется 0.

+0

Инициализация не выполняется в 'main', это происходит до ввода' main'. –

+0

Мой плохой, изменил его, спасибо! – speedyxvs

2

Не инициализированы переменные локального не static, что обычно означает, что они содержат мусор.

0 является столь же актуальным, как и любая другая ценность для мусора. Но начальное значение могло бы так же легко быть 42 или -12345.

Просто не делайте этого. Вы должны убедиться, что вы не читаете значение какой-либо переменной, если оно не было инициализировано. Чтение неинициализированной переменной имеет неопределенное поведение, а это означает, что возможное поведение не ограничивается печатью какого-либо произвольного значения.

Хороший способ сделать, чтобы сделать это, чтобы использовать явный инициализатор:

int n = 0; 

(Кстати, вам не хватает необходимого #include <stdio.h>, и правильное заявление для main является int main(void).)

+0

Может иметь значение [0x2A] (http://en.wikipedia.org/wiki/Phrases_from_The_Hitchhiker's_Guide_to_the_Galaxy#The_number_42). – chux

+0

Таким образом, переменная DOES имеет значение, а не какой-то конкретный - получила его. –

+0

@ScottHunter: по крайней мере, по состоянию на 2011 год, поведение чтения неинициализированного объекта не определено, поэтому нет, оно не обязательно имеет значение. –

-1

Когда вы объявляете локальную переменную, это похоже на то, что вы запрашиваете пространство памяти (в данном случае в стеке) для хранения значения. Поскольку это пространство памяти, оно всегда имеет значение. Это значение, вероятно, задается другим потоком, программой или системой или предыдущей функцией или т. Д. Фактически эти значения обычно рассматриваются как GARBAGE, потому что вы не установили его, и его значение является случайным.

В вашем коде функция f() рекурсивна, и на выходе отображается значение стека до запуска стековой памяти (переполнение стека).

+0

У автоматической переменной не должно быть места в памяти. На самом деле, он не должен даже иметь значение. Его использование может привести к сбою. Поведение просто * undefined *. –

Смежные вопросы