Объявление статической переменной уровня функции внутри блока if, который никогда не выполняется

Я понимаю статические переменные, объявленные внутри функции:

  1. Если начальное значение не указано, статическая переменная будет находиться в .bss, иначе в .data
  2. The memory for statics are allocated along with globals - i.e., well before the execution enters main
    • are these two assumptions correct ?
  3. Когда выполнение попадает в функцию в первый раз, статика инициализируется заданным пользователем значением (или нулем, если начальное значение не указано).
  4. ... и они сохраняют свои значения при последующих вызовах функции

Но что, если я объявлю свою статическую переменную внутри блока if? Я предполагаю, что моя третья точка должна быть обновлена ​​до "когда выполнение достигает строки, где объявлена ​​статическая переменная, они инициализируются ..." - я прав ?

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

Я написал две функции, чтобы попытаться понять, что происходит:

#include <stdio.h>

void foo()
{
    static foo_var_out;
    if(0){
        static foo_var_in_0;
        printf("%d %d\n", foo_var_in_0);
    } else {
        static foo_var_in_1;
        printf("%d %d\n", foo_var_in_1);
    }   
}

static void bar(int flag)
{
    static bar_var_out;
    if(flag){
        static bar_var_in_0;
        printf("%d %d\n", bar_var_in_0);
    } else {
        static bar_var_in_1;
        printf("%d %d\n", bar_var_in_1);
    }   
}

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

И взял дамп объекта:

$ gcc -o main main.c
$ objdump -t main | grep var
45:080495c0 l     O .bss    00000004              foo_var_in_1.1779
46:080495c4 l     O .bss    00000004              foo_var_out.1777
47:080495c8 l     O .bss    00000004              bar_var_in_1.1787
48:080495cc l     O .bss    00000004              bar_var_in_0.1786
49:080495d0 l     O .bss    00000004              bar_var_out.1785

Судя по выходным данным, foo_var_in_0 вообще не был создан (предположительно, потому что он находится внутри явного if(0)), тогда как bar_var_in_0 был создан (так как вызывающий может передать ненулевое значение, хотя единственный вызывающий явно передает нуль).

Думаю, мой вопрос: правильно ли предполагать, что для переменной foo_var_in_0 вообще не выделялась память? Я спрашиваю об этом конкретном случае; Правильно ли я читаю objdump - или мне нужно сделать что-то еще, чтобы проверить, займет ли переменная некоторая память во время выполнения программы?

Другими словами, если строка, в которой объявляется статическая переменная уровня функции, никогда не выполняется, действительно ли эта переменная вообще объявляется?

Если он вообще не будет создан, это в соответствии со стандартом C (менее вероятно) или оптимизацией времени компиляции и на каком уровне - как мне включить / выключить его (в gcc 4.1.1)?

Я понимаю, что один int - это не проблема, но меня больше интересует, как он работает; кроме того, что, если бы переменная была большим массивом размера, скажем, 5000 элементов 10-байтовой структуры?


c
person Amarghosh    schedule 29.11.2010    source источник
comment
Оба созданы для меня: pastebin.com/6cRbHWMJ (mingw gcc 4.5.1)   -  person Kos    schedule 29.11.2010


Ответы (3)


правильно ли предполагать, что для переменной foo_var_in_0 вообще не выделялась память?

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

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

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

person Lou Franco    schedule 29.11.2010
comment
Основываясь на данных objdump в этом конкретном случае (я понимаю, что неразумно полагаться на это, но чтобы подтвердить, что я правильно читаю objdump), я могу с уверенностью предположить, что для этой переменной не было выделено места - это что правильно? Или есть что-то еще, что мне нужно сделать, чтобы проверить, будет ли переменная сохранять свою память во время работы? - person Amarghosh; 29.11.2010
comment
Я считаю, что вы правильно прочитали objdump и что статика никогда не выделялась. Я думаю, что в этом случае для этого компилятора в этой компиляции вы можете предположить, что не было зарезервировано места. Вы не можете предполагать, что так будет в будущем, если не будете постоянно проверять это. - person Lou Franco; 29.11.2010
comment
Спасибо - и я получаю сообщение; не буду полагаться на такое поведение :) - person Amarghosh; 29.11.2010
comment
Можно ли получить контроль над этой оптимизацией - соответствует ли она какому-либо конкретному уровню оптимизации gcc? - person Amarghosh; 10.12.2010
comment
Если вас так сильно волнует машинный код, вам следует писать ассемблер. Я не уверен, зачем вам это нужно, но C не был разработан для такого рода точечного контроля над исполняемым файлом. Цели разработки C несовместимы с этой потребностью. - person Lou Franco; 10.12.2010
comment
Я просто пытался посмотреть, можно ли отключить его с помощью флага компилятора - просто из любопытства / академического интереса, если хотите. - person Amarghosh; 14.12.2010

Стандарт C не предписывает, где размещать переменные и прочее. Он просто предписывает, что соответствующая реализация должна иметь эквивалентное поведение (по отношению к эталонному поведению, которое определено стандартом), где «эквивалент» также определяется стандартом.

Итак, простой ответ заключается в том, что это оптимизация, и то, как ее включить / выключить, зависит от конкретного компилятора.

Реализация, которая выполняет межпроцедурный анализ, вероятно, также сможет избавиться от bar_var_in_0.

person lijie    schedule 29.11.2010
comment
Это имеет больше смысла - это простое удаление недостигнутого кода - ничего специфического для статики. - person Amarghosh; 29.11.2010

Просто чтобы добавить к правильным ответам других. Ваши предположения об инициализации static переменных неверны.

  • Переменные со статической памятью всегда инициализируются. Либо явно, если вы предоставляете инициализатор, либо неявно из 0.
  • Инициализатором для такой переменной всегда должно быть выражение константы времени компиляции. Таким образом, значение вычисляется во время компиляции и записывается непосредственно в объект. (Ну, если все равно нулю, в некоторых системах есть уловки / специальные разделы, которые избегают явного хранения переменной в объектном файле.)
  • Итак, нет, не будет выполняться оператор инициализатора при первом доступе к переменной (как система узнает, по значению другой статической переменной?), Но всякий раз, когда вы запускаете программу, переменная уже инициализируется.
person Jens Gustedt    schedule 29.11.2010
comment
Спасибо - я не знал, что инициализатор должен быть постоянной времени компиляции. - person Amarghosh; 29.11.2010