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

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

int globalgarbage;
unsigned int anumber = 42;

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

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

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

Ответ 1

Мне было интересно, поэтому я написал следующую тестовую программу и скомпилировал ее с помощью 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

Ответ 2

Некоторые соответствующие формулировки из стандарта С++:

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). В противном случае объект инициализируется, первый элемент управления проходит через его выражение; такой объект считается инициализированным на завершение его инициализации. Если инициализация завершается исключение, инициализация не завершена, поэтому она будет снова повторите попытку, когда в следующий раз элемент управления войдет в декларацию. Если управление повторно вводит декларацию (рекурсивно), пока объект находится инициализировано, поведение undefined. [Пример:

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

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

5

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

  • Заключение (для g++, среда Windows):

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

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

включают <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;
    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?

Ответ 6

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

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

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

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

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

Ответ 7

Или он инициализируется при первом вызове 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. При первом вызове переменная будет инициализирована. Затем, при первом и следующем вызовах, его значение будет возвращено (по ссылке).