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

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

  1. Если начальное значение не указано, статическая переменная будет находиться в .bss, в противном случае в .data
  2. Память для статики выделяется вместе с глобальными переменными, т. Е. Задолго до того, как исполнение входит в main
    • правильны ли эти два предположения ?
  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-байтовой структуры?

Ответы [ 3 ]

3 голосов
/ 29 ноября 2010

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

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

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

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

2 голосов
/ 29 ноября 2010

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

  • Переменные со статическим хранилищем всегда инициализируются.Либо явно, если вы предоставляете инициализатор, либо неявно из 0.
  • Инициализатор для такой переменной всегда должен быть выражением константы времени компиляции.Таким образом, значение вычисляется во время компиляции и записывается непосредственно в объект.(Хорошо, если все это ноль, у некоторых систем есть хитрости / специальные разделы, которые избегают явного хранения переменной в объектном файле.)
  • Так что нет, оператор инициализатора не будет выполняться, когда переменнаядоступ в первый раз (как система узнает, по значению другой статической переменной?), но всякий раз, когда вы запускаете программу, переменная уже инициализируется.
2 голосов
/ 29 ноября 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...