함수 수준 정적 변수는 언제 할당 / 초기화됩니까?


89

저는 전역 적으로 선언 된 변수가 프로그램 시작 시간에 할당 (적용 가능한 경우 초기화)된다는 것을 확신합니다.

int globalgarbage;
unsigned int anumber = 42;

하지만 함수 내에 정의 된 정적 요소는 어떻습니까?

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

공간은 언제 globalish할당됩니까? 프로그램이 언제 시작되는지 추측하고 있습니다. 하지만 그때도 초기화됩니까? 아니면 doSomething()처음 호출 될 때 초기화 됩니까?

답변:


91

궁금해서 다음 테스트 프로그램을 작성하고 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

30
설명 : 정적 변수는 포함하는 함수가 호출 될 때가 아니라 실행이 처음 선언에 도달 할 때 초기화됩니다. 함수의 시작 부분에 정적이있는 경우 (예 : 예제에서) 이것들은 동일하지만 반드시 그런 것은 아닙니다. 예를 들어 'if (...) {static MyClass x; ...} '이면 if 문의 조건이 false로 평가되는 경우 해당 함수를 처음 실행하는 동안'x '가 ALL에서 초기화되지 않습니다.
EvanED

4
그러나 이것은 정적 변수가 사용될 때마다 프로그램이 이전에 사용되었는지 확인해야하기 때문에 런타임 오버 헤드로 이어지지 않습니까? 그렇지 않다면 초기화해야하기 때문입니까? 그런 경우에는 약간 짜증이납니다.
HelloGoodbye

완벽한 그림
Des1gnWizard

@veio : 예, 초기화는 스레드로부터 안전합니다. 자세한 내용은 해당 질문을 참조하십시오 : stackoverflow.com/questions/23829389/…
Rémi

2
@HelloGoodbye : 예, 런타임 오버 헤드가 발생합니다. 또한 다음 질문을 참조하십시오. stackoverflow.com/questions/23829389/…
Rémi

53

C ++ 표준의 관련 설명 :

3.6.2 로컬이 아닌 객체의 초기화 [basic.start.init]

1

정적 저장 기간 ( basic.stc.static )이있는 객체의 저장소는 다른 초기화가 발생하기 전에 0으로 초기화되어야합니다 ( dcl.init ). 정적 저장 기간이 상수 표현식 ( expr.const )으로 초기화 된 POD 유형 ( basic.types )의 객체는 동적 초기화가 발생하기 전에 초기화되어야합니다. 동일한 번역 단위에서 정의되고 동적으로 초기화 된 정적 저장 기간을 가진 네임 스페이스 범위의 개체는 해당 정의가 번역 단위에 나타나는 순서대로 초기화됩니다. [참고 : dcl.init.aggr 집계 멤버가 초기화되는 순서를 설명합니다. 로컬 정적 개체의 초기화는 stmt.dcl에 설명되어 있습니다 . ]

[컴파일러 작성자를위한 더 많은 자유를 추가하는 아래 더 많은 텍스트]

6.7 선언문 [stmt.dcl]

...

4

다른 초기화가 발생하기 전에 정적 저장 기간 ( basic.stc.static )이 있는 모든 로컬 객체 의 제로 초기화 ( dcl.init )가 수행됩니다. 상수 표현식으로 초기화 된 정적 저장 기간 이있는 POD 유형 ( basic.types ) 의 로컬 객체는 해당 블록이 처음 입력되기 전에 초기화됩니다. 구현이 네임 스페이스 범위 ( basic.start.init). 그렇지 않으면 이러한 개체는 처음 컨트롤이 선언을 통과 할 때 초기화됩니다. 이러한 객체는 초기화 완료시 초기화 된 것으로 간주됩니다. 예외가 발생하여 초기화가 종료되면 초기화가 완료되지 않았으므로 다음에 컨트롤이 선언에 들어갈 때 다시 시도됩니다. 개체가 초기화되는 동안 컨트롤이 (재귀 적으로) 선언을 다시 입력하면 동작이 정의되지 않습니다. [ 예 :

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

- 최종 예 ]

5

정적 저장 기간이있는 로컬 개체의 소멸자는 변수가 생성 된 경우에만 실행됩니다. [참고 : basic.start.term 은 정적 저장 기간이있는 로컬 개체가 삭제되는 순서를 설명합니다. ]


이것은 내 질문에 대한 대답이며 받아 들여진 대답과 달리 "일례적인 증거"에 의존하지 않습니다. 특별히 정적으로 초기화 된 함수 로컬 정적 개체의 생성자에서 예외에 대한 언급을 찾고있었습니다.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

모든 정적 변수에 대한 메모리는 프로그램로드시 할당됩니다. 그러나 로컬 정적 변수는 프로그램 시작 시가 아니라 처음 사용될 때 생성되고 초기화됩니다. 그것에 대한 좋은 읽기와 일반적으로 정적이 여기에 있습니다 . 일반적으로 이러한 문제 중 일부는 구현에 의존한다고 생각합니다. 특히 메모리에서이 항목이 어디에 있는지 알고 싶은 경우에는 더욱 그렇습니다.


2
정답은 아닙니다. 로컬 정적이 할당되고 "프로그램로드시"0으로 초기화 된 다음 (따옴표로 묶인 것도 맞지 않기 때문입니다), 그런 다음 해당 함수가 처음 입력 될 때 다시 초기화됩니다.
Mooing Duck 2012

7 년이 지난 지금 링크가 끊어진 것 같습니다.
Steve

1
네, 링크가 끊어졌습니다. 아카이브는 다음과 같습니다. web.archive.org/web/20100328062506/http://www.acm.org/…
Eugene

10

컴파일러는 foo프로그램로드시 함수 에 정의 된 정적 변수를 할당하지만 컴파일러는 함수 에 몇 가지 추가 명령 (기계 코드)을 추가 foo하여 처음 호출 될 때이 추가 코드가 정적 변수를 초기화합니다 ( 예 : 해당되는 경우 생성자 호출).

@Adam : 컴파일러에 의한 코드 주입이면에서 이것이 당신이 본 결과의 이유입니다.


5

Adam Pierce의 코드를 다시 테스트 하고 클래스의 정적 변수와 POD 유형의 두 가지 사례를 추가했습니다. 내 컴파일러는 Windows OS (MinGW-32)에서 g ++ 4.8.1입니다. 결과는 클래스의 정적 변수가 전역 변수와 동일하게 처리됩니다. 생성자는 주 함수에 들어가기 전에 호출됩니다.

  • 결론 (g ++, Windows 환경의 경우) :

    1. class : constructor의 전역 변수정적 멤버는 함수 (1)에 들어가기 전에 호출 됩니다.
    2. 로컬 정적 변수 : 생성자는 실행이 처음 선언에 도달 할 때만 호출됩니다.
    3. 경우 지역 정적 변수 POD 타입 , 그것은 또한 입력하기 전에 초기화되는 주요 기능 (1) . POD 유형의 예 : static int number = 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 환경에서 테스트 했습니까?


3

정적 변수는 코드 세그먼트 내부에 할당되며 실행 가능 이미지의 일부이므로 이미 초기화되어 매핑됩니다.

함수 범위 내의 정적 변수는 동일하게 처리되며 범위 지정은 순전히 언어 수준 구조입니다.

이러한 이유로 정적 변수는 정의되지 않은 값이 아닌 0 (다른 것을 지정하지 않는 한)으로 초기화됩니다.

초기화에 대한 몇 가지 다른 측면이 있습니다. 예를 들어 공유 세그먼트를 사용하면 한 번에 실행되는 실행 파일의 여러 인스턴스가 동일한 정적 변수에 액세스 할 수 있습니다.

C ++ (전역 범위)에서 정적 개체에는 C 런타임 라이브러리의 제어하에 프로그램 시작의 일부로 호출되는 생성자가 있습니다. Visual C ++에서 개체가 초기화되는 순서는 init_seg pragma 로 제어 할 수 있습니다 .


4
이 질문은 함수 범위의 정적에 관한 것입니다. 최소한 중요하지 않은 생성자가 있으면 함수에 처음 입력 할 때 초기화됩니다. 또는 더 구체적으로 해당 라인에 도달했을 때.
Adam Mitz

사실이지만 질문은 변수에 할당 된 공간에 대해 이야기하고 간단한 데이터 유형을 사용합니다. 공간은 여전히 ​​코드 세그먼트에 할당되어 있습니다
Rob Walker

여기서 코드 세그먼트 대 데이터 세그먼트가 실제로 어떻게 중요한지 모르겠습니다. OP의 설명이 필요하다고 생각합니다. 그는 "해당되는 경우 초기화"라고 말했습니다.
아담 미츠

5
변수는 코드 세그먼트 내부에 할당되지 않습니다. 이렇게하면 쓰기가 불가능합니다.
botismarius

1
정적 변수는 초기화 여부에 따라 데이터 세그먼트 또는 bss 세그먼트에 공간이 할당됩니다.
EmptyData

3

아니면 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 블록 내에서 사용합니다. 첫 번째 호출에서 변수가 초기화됩니다. 그런 다음 첫 번째 및 다음 호출에서 해당 값이 참조로 반환됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.