Fonksiyon düzeyinde statik değişkenler ne zaman tahsis edilir / başlatılır?


91

Global olarak bildirilmiş değişkenlerin programın başlama saatinde tahsis edileceğinden (ve varsa başlatılacağından) oldukça eminim.

int globalgarbage;
unsigned int anumber = 42;

Peki ya bir işlev içinde tanımlanan statik olanlar?

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

Alan ne zaman globalishtahsis edilir? Programın ne zaman başlayacağını tahmin ediyorum. Ama o zaman da başlatılıyor mu? Yoksa doSomething()ilk çağrıldığında başlatıldı mı?

Yanıtlar:


93

Merak ettiğim için aşağıdaki test programını yazdım ve g ++ sürüm 4.1.2 ile derledim.

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;
}

Sonuçlar beklediğim gibi değildi. Statik nesnenin yapıcısı, işlev ilk çağrılana kadar çağrılmadı. İşte çıktı:

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

31
Açıklık getirmek gerekirse: statik değişken, içeren işlev çağrıldığında değil, yürütme ilk kez bildirimine ulaştığında başlatılır. İşlevin başında sadece bir statik varsa (örneğin, örneğinizde) bunlar aynıdır, ancak zorunlu değildir: örneğin, 'if (...) {statik Sınıfım x; ...} 'ise, if ifadesinin koşulunun yanlış olarak değerlendirildiği durumda, bu işlevin ilk çalıştırılması sırasında' x 'ALL'de başlatılmayacaktır.
EvanED

4
Ancak bu, statik değişken her kullanıldığında, programın daha önce kullanılıp kullanılmadığını kontrol etmesi gerektiğinden, çalıştırma zamanı ek yüküne yol açmaz mı? Bu durumda bu biraz berbat.
HelloGoodbye

mükemmel illüstrasyon
Des1gnWizard

@veio: Evet, başlatma iş parçacığı açısından güvenlidir. Daha fazla ayrıntı için bu soruya bakın: stackoverflow.com/questions/23829389/…
Rémi

2
@HelloGoodbye: evet, bir çalışma zamanı ek yüküne yol açar. Ayrıca şu soruya bakın: stackoverflow.com/questions/23829389/…
Rémi

54

C ++ Standardından bazı ilgili sözler:

3.6.2 Yerel olmayan nesnelerin başlatılması [basic.start.init]

1

Statik depolama süresi olan nesneler için depolama ( temel.stc.static ) , başka herhangi bir başlatma gerçekleşmeden önce sıfır başlatılacaktır ( dcl.init ). Sabit ifadelerle (ifade .const ) başlatılan statik depolama süresine sahip POD türlerinin (temel türler ) nesneleri, herhangi bir dinamik başlatma gerçekleşmeden önce başlatılmalıdır. Aynı çeviri biriminde tanımlanan ve dinamik olarak başlatılan statik depolama süresine sahip ad alanı kapsamının nesneleri, tanımlarının çeviri biriminde göründüğü sıraya göre başlatılacaktır. [Not: dcl.init.aggr toplu üyelerin başlatıldığı sırayı açıklar. Yerel statik nesnelerin başlatılması stmt.dcl'de açıklanmıştır . ]

[derleyici yazarlar için daha fazla özgürlük ekleyen daha fazla metin]

6.7 Beyan beyanı [stmt.dcl]

...

4

Statik depolama süresi ( ) olan tüm yerel nesnelerin sıfır başlatılması ( dcl.init ). Aksi takdirde, böyle bir nesne başlatılır, kontrol ilk kez bildiriminden geçer; böyle bir nesne, başlatılmasının tamamlanmasının ardından başlatılmış kabul edilir. Başlatma bir istisna atılarak çıkarsa, başlatma tamamlanmaz, bu nedenle kontrol bildirime bir sonraki girdiğinde yeniden denenir. Nesne başlatılırken denetim bildirimi (özyinelemeli olarak) yeniden girerse, davranış tanımsızdır. [ Örnek: basic.stc.static başka başlatma yapılmadan önce) gerçekleştirilir. Sabit ifadelerle başlatılan statik depolama süresine sahip yerel bir POD türü (temel türler ) nesnesi , bloğu ilk kez girilmeden önce başlatılır. Bir uygulamanın, ad alanı kapsamındaki ( temel.start.init

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

- son örnek ]

5

Statik depolama süresi olan yerel bir nesnenin yıkıcısı, yalnızca ve yalnızca değişken oluşturulmuşsa çalıştırılır. [Not: basic.start.term , statik depolama süresi olan yerel nesnelerin yok edilme sırasını açıklar. ]


Bu, sorumu yanıtladı ve kabul edilen cevabın aksine "anekdot niteliğinde kanıtlara" dayanmıyor. Statik olarak başlatılmış fonksiyon yerel statik nesnelerin yapıcısındaki istisnalardan özellikle bahsetmeyi arıyordum:If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration.
Bensge

26

Tüm statik değişkenler için bellek, program yükünde tahsis edilir. Ancak yerel statik değişkenler, program başlangıcında değil, ilk kullanıldıklarında oluşturulur ve başlatılır. Bununla ilgili iyi okumalar var ve burada genel olarak statik var . Genel olarak, bu sorunlardan bazılarının uygulamaya bağlı olduğunu düşünüyorum, özellikle de bu şeylerin hafızada nereye yerleştirileceğini bilmek istiyorsanız.


2
tam olarak değil, yerel statikler tahsis edilir ve "program yükünde" sıfır olarak başlatılır (tırnak içinde, çünkü bu da tam olarak doğru değildir) ve sonra içinde bulundukları fonksiyon ilk kez girildiğinde yeniden başlatılır.
Mooing Duck

Görünüşe göre bu bağlantı 7 yıl sonra kesildi.
Steve

1
Evet, bağlantı koptu. İşte bir arşiv: web.archive.org/web/20100328062506/http://www.acm.org/…
Eugene

10

Derleyici foo, program yüklenirken bir işlevde tanımlanan statik değişken (ler) i tahsis eder , ancak derleyici ayrıca işlevinize bazı ek talimatlar (makine kodu) da ekler.foo böylece ilk kez çağrıldığında bu ek kod statik değişkeni başlatır örneğin, yapıcıyı çağırmak, varsa).

@Adam: Gördüğünüz sonucun nedeni derleyici tarafından perde arkasına kod enjeksiyonu yapılmasıdır.


5

Adam Pierce'den kodu tekrar test etmeye çalışıyorum ve iki durum daha ekledim: sınıfta statik değişken ve POD türü. Derleyicim Windows işletim sisteminde (MinGW-32) g ++ 4.8.1. Sonuç, sınıftaki statik değişkenin genel değişkenle aynı şekilde ele alınmasıdır. Yapıcısı, ana işleve girmeden önce çağrılacaktır.

  • Sonuç (g ++, Windows ortamı için):

    1. Global değişken ve sınıftaki statik üye : yapıcı, enter'dan önce çağrılır ana işlevi (1) .
    2. Yerel statik değişken : yapıcı yalnızca yürütme ilk kez bildirimine ulaştığında çağrılır.
    3. Eğer Yerel statik değişkeni POD türüdür , o zaman da girmeden önce başlatılır ana fonksiyonu (1) . POD tipi için örnek: statik int sayı = 10;

(1) : Doğru durum "aynı çeviri biriminden herhangi bir işlev çağrılmadan önce" olmalıdır.Bununla birlikte, aşağıdaki örnekte olduğu gibi basit için, o zaman ana işlevdir.

<iostream> dahil

#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;
 }

sonuç:

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 ortamında test edilen var mı?


4

Yoksa doSomething () ilk kez çağrıldığında mı başlatılır?

Evet öyle. Bu, diğer şeylerin yanı sıra, küresel olarak erişilen veri yapılarını uygun olduğunda, örneğin dene / yakala bloklarının içinde başlatmanıza izin verir. Örneğin

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

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

Yazabilirsin

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

ve dene / yakala bloğunun içinde kullanın. İlk çağrıda, değişken başlatılacaktır. Daha sonra, ilk ve sonraki aramalarda değeri (referans olarak) döndürülür.


3

Statik değişkenler bir kod segmenti içinde tahsis edilir - bunlar çalıştırılabilir görüntünün bir parçasıdır ve bu nedenle önceden başlatılmış olarak eşlenir.

İşlev kapsamındaki statik değişkenler aynı şekilde ele alınır, kapsam tamamen dil düzeyinde bir yapıdır.

Bu nedenle, statik bir değişkenin tanımsız bir değer yerine (başka bir şey belirtmediğiniz sürece) 0 olarak başlatılacağı garanti edilir.

Başlatma için yararlanabileceğiniz başka yönler de vardır - örneğin paylaşılan bölümler, aynı statik değişkenlere erişmek için aynı anda çalışan yürütülebilir dosyanızın farklı örneklerine izin verir.

C ++ 'da (genel kapsamlı) statik nesnelerin oluşturucuları, C çalışma zamanı kitaplığının kontrolü altında program başlangıcının bir parçası olarak adlandırılır. Visual C ++ altında, en azından nesnelerin başlatıldığı sıra init_seg pragma tarafından kontrol edilebilir .


4
Bu soru, işlev kapsamlı statik hakkındadır. En azından önemsiz yapıcılara sahip olduklarında, fonksiyona ilk girişte başlatılırlar. Ya da daha spesifik olarak, bu çizgiye ulaşıldığında.
Adam Mitz

Doğru - ancak soru değişkene ayrılan alandan bahsediyor ve basit veri türlerini kullanıyor. Alan hala kod segmentinde tahsis
Rob Walker

Veri segmentine karşı kod segmentinin burada gerçekten önemli olduğunu anlamıyorum. Bence OP'den açıklamaya ihtiyacımız var. "Varsa başlattı" dedi.
Adam Mitz

5
değişkenler hiçbir zaman kod segmenti içinde tahsis edilmez; bu şekilde yazılabilir olmayacaklardı.
botismarius

1
Statik değişkenler, başlatılıp başlatılmadıklarına bağlı olarak veri segmentinde veya bss segmentinde alan tahsis edilir.
EmptyData
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.