Страницы

Поиск по вопросам

понедельник, 25 ноября 2019 г.

Время создания переменной


Например, есть искусственный пример:
...

int main() {
    /*
        некоторый код
    */
    ...{
        static int someVal = 42;
    }
}


Когда произойдет создание переменной? Во время запуска программы или когда доберемся до нужной инструкции? 
    


Ответы

Ответ 1



Вопрос не имеет однозначного ответа, ибо ответ на него в общем случае критическ зависит от типа объекта. В вашем конкретном примере с типом int - объект всегда создается строго во время запуска программы. А в общем случае это зависит от того, что вы называете "созданием". У объектов в С++ есть "период хранения" (storage duration) и "время жизни" (lifetime). Период хранения - это период выделения-освобождения "сырой" памяти, занимаемой объектом. Период хранения любых статических объектов - это всегда безусловно все время выполнения программы. Время жизни - это период между [нетривиальной] инициализацией и [нетривиальной] деструкцие объекта в этой сырой памяти. Время жизни всегда вложено в период хранения. Время жизни зависит от свойств объекта. Если инициализация объекта тривиальна (т.е. если это не-класс тип или класс тип инициализируемый тривиальным конструктором по умолчанию), то конструкции не требуется вообще и начало периода хранения автоматически является началом времени жизни. Аналогично если деструкция объекта тривиальна (т.е. если это не-класс тип или класс тип с тривиальным деструктором), то время жизни длится до конца периода хранения. Если статический объект требует нетривиальной конструкции, то она будет выполнен в момент первого прохода процесса выполнения по объявлению объекта (если такой момент вообще наступит). Также, если объект скалярного типа (такого как тип int) инициализируется константны выражением, то такая инициализация будет выполнена статически (еще на старте программы) Если же выражение неконстантно, на старте программы такой объект будет инициализирован нулем, а инициализация выражением будет выполнена позже, динамически. В случае объекта, объявленного локально, это произойдет в момент первого прохода процесса выполнения по объявлению объекта (если такой момент вообще наступит). В вашем случае вы имеет дело со статическим объектом типа int. Период хранения таког объекта - все время выполнения программы. И так как это не-класс тип, время жизни этог объекта - все время выполнения программы. Также язык гарантирует вам, что инициализация этого объекта делается статически - не во время первого прохода выполнения по объявлению этого объекта, а сразу на старте программы. Например, вот такой код гарантированно выведет 42 // Продолжительность хранения `i` уже началась // Время жизни `i` уже началось // `i` уже проинициализировано значением 42 int main() { goto skip; static int i = 42; // Статическая инициализация skip: std::cout << i << std::endl; } А вот такой гарантированно выведет 0 // Продолжительность хранения `i` уже началась // Время жизни `i` уже началось // `i` уже проинициализировано значением 0 int main() { goto skip; static int i = std::rand(); // Динамическая инициализация skip: std::cout << i << std::endl; } Если вы замените int на std::string, то период хранения такого объекта - все равн все время выполнения программы. А вот время жизни этого объекта начнется тогда, когда выполнение в первый раз пройдет по его объявлению (если такой момент вообще наступит). Например, вот такой код // Продолжительность хранения `s` уже началась // Время жизни `s` еще не началось // Однако все члены `s` уже проинициализированы нулями int main() { goto skip; static std::string s = "Hello World!"; // Динамическая инициализация и начало жизни skip: std::cout << s << std::endl; } увидит объект s, по которому уже успел проехаться каток огульной нулевой инициализации но конструкции еще не выполнялось. Время жизни объекта так и не началось. Поведение не определено.

Ответ 2



При первом выполнении кода. Можно убедиться, создавая, например, переменную некоторого класса с конструктором. struct Test { Test(int x) { cout << x << endl; } }; int main() { for(int i = 0; i < 10; ++i) { cout << i << endl; static Test t = 42; } } Получим 0 42 1 2 3 4 5 6 7 8 9 А что такое создание переменной, как не вызов конструктора? :) Update Так вот, уточняем - что такое создание переменной? Выделенное место в памяти можн ли считать созданием переменной? вряд ли, потому что в общем случае это всего лишь место в памяти, которое становится переменной определенного типа только после трактовки данного места в памяти как переменной данного типа. Скорее всего - хотя от компилятора это и не требуется! - место для такой переменно будет выделено еще на этапе компиляции. Вполне вероятно, что для простого типа врод int, double или char[] - память будет сразу же соответствующим образом заполнена. Н это не означает создания переменной. Как переменная этот кусок памяти будет трактоваться при первом обращении к ней, при ее первом входе в область видимости. Если это что-то посложнее простого int - то первое обращение будет заключаться в вызове конструктора. Если нет - конструктор вызван не будет, но, независимо от типа переменная возникает только сейчас! Потому что никто не запрещает - никакой стандарт - выделить компилятору под эту переменну динамическую память при первом выполнении функции. То, как поступают реальные компиляторы, никакого отношения к вопросу создания переменной не имеет. Надеюсь, я более-менее понятно донес мысль? И напомню еще раз - компилятор имеет огромные права по переиначиванию программы - лишь бы она работала так, как будто она работает, как написано.

Ответ 3



Подозреваю, что зависит от дальнейшего кода и/или настроек компилятора. Такой пример static int someVal = 42; printf ("0x%X", someVal); Дает на выходе такой код: .text:0040101E push 42 .text:00401020 push offset Format ; "0x%X" .text:00401025 call esi ; __imp__printf То есть переменной как таковой и нет, компилятор соптимизировал ее в константу. А вот такой код: printf ("0x%X", someVal); someVal++; printf ("0x%X", someVal); Приводит к созданию глобальной переменной, инициализированной во время компиляции.

Ответ 4



Странно, что на такой простой вопрос фактически никто не дал прямого нормальног ответа. Масса рассуждений о времени жизни и какой то "сырой" памяти. Даже как то грустно. На самом деле последний ответ ближе всего к правильному, но какой то неуверенный. В итоге ответ такой: Все глобальные и статические переменные не требующие конструирования создаются и инициализируются на этапе компиляции. И, как правило, хранятся в сегменте данных, либо оптимизируются до константы прямо в сегменте кода, если к ним нет обращения из других мест. Все локальные переменные или по другому динамические, не требующие конструирования как правило создаются и инициализируются в стеке своей локализации. Т.е. переменная внутри функции создается в стеке этой функции. Такие переменные создаются при вхождении в функцию или блок и уничтожаются при выходе во время выполнения программы. Все переменные (объекты) требующие конструирования, соответственно конструируются во время выполнения программы. Глобальные во время старта программы, локальные после их создания в месте их локализации. Теперь, что касается данного вопроса int main() { /* некоторый код */ ...{ static int someVal = 42; } } Переменная someVal будет создана и инициализирована во время компиляции. Если к не не будет обращений то и вовсе будет нивелирована до константы или даже удалена. Ели к ней будет обращение то, скорее всего, она будет размещена в сегменте данных. На этапе компиляции обращение к данной переменной будет ограничено только из ее блока И так как к ней в данном конкретном случае нет никаких обращений то она будет просто удалена даже не попав в код программы.

Комментариев нет:

Отправить комментарий