Когда статические переменные уровня функции выделяются / инициализируются? - PullRequest
80 голосов
/ 11 сентября 2008

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

int globalgarbage;
unsigned int anumber = 42;

А как насчет статических, определенных в функции?

void doSomething()
{
  static bool globalish = true;
  // ...
}

Когда выделяется место для globalish? Я предполагаю, когда программа запускается. Но это тоже инициализируется? Или он инициализируется при первом вызове doSomething()?

Ответы [ 7 ]

78 голосов
/ 11 сентября 2008

Мне было любопытно, поэтому я написал следующую тестовую программу и скомпилировал ее с g ++ версии 4.1.2.

include <iostream>
#include <string>

using namespace std;

class test
{
public:
        test(const char *name)
                : _name(name)
        {
                cout << _name << " created" << endl;
        }

        ~test()
        {
                cout << _name << " destroyed" << endl;
        }

        string _name;
};

test t("global variable");

void f()
{
        static test t("static variable");

        test t2("Local variable");

        cout << "Function executed" << endl;
}


int main()
{
        test t("local to main");

        cout << "Program start" << endl;

        f();

        cout << "Program end" << endl;
        return 0;
}

Результаты оказались не такими, как я ожидал. Конструктор для статического объекта не вызывался до первого вызова функции. Вот вывод:

global variable created
local to main created
Program start
static variable created
Local variable created
Function executed
Local variable destroyed
Program end
local to main destroyed
static variable destroyed
global variable destroyed
47 голосов
/ 12 сентября 2008

Некоторые соответствующие слова из C ++ Standard:

3.6.2 Инициализация нелокальных объектов [basic.start.init]

1

Хранилище для объектов со статическим хранилищем длительность ( basic.stc.static ) должна быть инициализирована нулями ( dcl.init ) перед любой другой инициализацией. Объекты Типы POD ( basic.types ) со статической продолжительностью хранения инициализируется с помощью константных выражений ( expr.const ) должно быть инициализируется до любой динамической инициализации. Объекты области пространства имен со статической продолжительностью хранения, определенной в та же единица перевода и динамически инициализируется инициализируется в том порядке, в котором их определение появляется в переводческий блок. [Примечание: dcl.init.aggr описывает порядок, в котором агрегированные элементы инициализируются. инициализация локальных статических объектов описана в stmt.dcl . ]

[больше текста ниже, добавляя больше свобод для авторов компиляторов]

6.7 Заявление декларации [stmt.dcl]

...

4

Инициализация нуля ( dcl.init ) всех локальных объектов с статическая продолжительность хранения ( basic.stc.static ) выполняется до любая другая инициализация имеет место. Местный объект Тип POD ( basic.types ) со статической продолжительностью хранения инициализируется с помощью константных выражений блок впервые введен. Реализация разрешено выполнять ранняя инициализация других локальных объектов со статическим хранилищем продолжительность при тех же условиях, что реализация разрешено статически инициализировать объект со статическим хранилищем продолжительность в области имен ( basic.start.init ). Иначе такой объект инициализируется при первом прохождении управления через его заявление; такой объект считается инициализированным после завершение его инициализации. Если инициализация завершается выдает исключение, инициализация не завершена, поэтому повторить попытку в следующий раз, когда элемент управления войдет в декларацию. Если элемент управления повторно входит в объявление (рекурсивно), пока объект находится инициализировано, поведение не определено. [* * Пример тысяча тридцать восемь: * 1 039 *

      int foo(int i)
      {
          static int s = foo(2*i);  // recursive call - undefined
          return i+1;
      }

- конец примера ]

5

Деструктор для локального объекта со статической продолжительностью хранения будет выполняется, если и только если переменная была создана. [Примечание: basic.start.term описывает порядок, в котором локальный объекты со статической продолжительностью хранения уничтожаются. ]

23 голосов
/ 11 сентября 2008

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

9 голосов
/ 11 сентября 2008

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

@ Адам: Это закулисное внедрение кода компилятором является причиной результата, который вы увидели.

5 голосов
/ 03 декабря 2013

Я пытаюсь снова протестировать код из Адам Пирс и добавил еще два случая: статическая переменная в классе и тип POD. Мой компилятор g ++ 4.8.1, в ОС Windows (MinGW-32). Результат - статическая переменная в классе, которая обрабатывается так же, как и глобальная переменная. Его конструктор будет вызываться перед вводом основной функции.

  • Вывод (для g ++, среды Windows):

    1. Глобальная переменная и статический член в классе : конструктор вызывается перед вводом main function (1) .
    2. Локальная статическая переменная : конструктор вызывается только тогда, когда выполнение достигает своего объявления в первый раз.
    3. Если Локальная статическая переменная имеет тип POD , то она также инициализируется перед вводом main function (1) . Пример для типа POD: статический int номер = 10;

(1) : правильное состояние должно быть: ", прежде чем любая функция из того же модуля перевода будет вызвана". Однако для простых, как в примере ниже, тогда основная функция.

include

#include < string>

using namespace std;

class test
{
public:
   test(const char *name)
            : _name(name)
    {
            cout << _name << " created" << endl;
    }

    ~test()
    {
            cout << _name << " destroyed" << endl;
    }

    string _name;
    static test t; // static member
 };
test test::t("static in class");

test t("global variable");

void f()
{
    static  test t("static variable");
    static int num = 10 ; // POD type, init before enter main function

    test t2("Local variable");
    cout << "Function executed" << endl;
}

int main()
{
    test t("local to main");
    cout << "Program start" << endl;
    f();
    cout << "Program end" << endl;
    return 0;
 }

результат:

static in class created
global variable created
local to main created
Program start
static variable created
Local variable created
Function executed
Local variable destroyed
Program end
local to main destroyed
static variable destroyed
global variable destroyed
static in class destroyed

Кто-нибудь тестировал в Linux env?

3 голосов
/ 11 сентября 2008

Или он инициализируется при первом вызове doSomething ()?

Да, это так. Это, помимо прочего, позволяет инициализировать глобально доступные структуры данных, когда это уместно, например, внутри блоков try / catch. Например. вместо

int foo = init(); // bad if init() throws something

int main() {
  try {
    ...
  }
  catch(...){
    ...
  }
}

Вы можете написать

int& foo() {
  static int myfoo = init();
  return myfoo;
}

и используйте его внутри блока try / catch. При первом вызове переменная будет инициализирована. Затем при первом и следующем вызовах будет возвращено его значение (по ссылке).

3 голосов
/ 11 сентября 2008

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

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

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

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

В C ++ (глобальная область действия) статические объекты имеют свои конструкторы, вызываемые как часть запуска программы, под управлением библиотеки времени выполнения C. В Visual C ++, по крайней мере, порядок инициализации объектов может контролироваться прагмой init_seg .

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