2009-05-22 2 views
19

함수 내에서 정적 변수의 기본 구현에 대해 궁금합니다.정적 변수 초기화는 컴파일러에서 어떻게 구현됩니까?

기본 유형 (char, int, double 등)의 정적 변수를 선언하고 초기 값을 지정하면 컴파일러가 해당 변수의 값을 변수의 맨 처음에 간단하게 설정한다고 생각합니다. main() 전에 프로그램이 호출됩니다

void SomeFunction(); 

int main(int argCount, char ** argList) 
{ 
    // at this point, the memory reserved for 'answer' 
    // already contains the value of 42 
    SomeFunction(); 
} 

void SomeFunction() 
{ 
    static int answer = 42; 
} 

그러나, 정적 변수는 클래스의 인스턴스 인 경우 :

class MyClass 
{ 
    //... 
}; 

void SomeFunction(); 

int main(int argCount, char ** argList) 
{ 
    SomeFunction(); 
} 

void SomeFunction() 
{ 
    static MyClass myVar; 
} 

나는이 함수가 호출되는 것은 이번이 처음 때까지 초기화되지 않을 것이라는 점을 알고있다 . 컴파일러는 함수가 처음 호출 될 때를 알 수있는 방법이 없으므로 어떻게이 동작을 생성합니까? 그것은 본질적으로 함수 본문에 if 블록을 도입합니까?

static bool initialized = 0; 
if (!initialized) 
{ 
    // construct myVar 
    initialized = 1; 
} 

답변

11

컴파일러 출력에서 ​​필자는 함수의 로컬 정적 변수가 사용자가 상상하는대로 정확하게 초기화된다는 것을 알았습니다.

일반적으로 스레드 안전 모드에서 수행되는 이 아니라입니다. 따라서 여러 스레드에서 호출 될 수있는 정적 로컬이있는 함수가있는 경우이를 고려해야합니다. 다른 스레드가 호출되기 전에 해당 함수를 주 스레드에서 한 번 호출하면 해당 트릭이 수행됩니다.

로컬 정적 초기화가 예제처럼 간단한 상수 인 경우 컴파일러에서 이러한 선회를 수행 할 필요가 없다는 것을 추가해야합니다. 즉 이미지의 변수를 초기화하거나 main()처럼 초기화해야합니다. 정적 정적 초기화 (프로그램이 차이를 알 수 없으므로). 하지만 함수의 리턴 값으로 초기화한다면, 컴파일러는 초기화가 완료되었는지 여부를 나타내는 플래그를 거의 테스트해야합니다.

+0

변경 되었습니까? 나는 C++ 11 이후 정적 초기화가 스레드로부터 안전하다는 말을 들었다. –

+0

@VictorPolevoy : 예 -이 대답이 작성되면 C++ 11이 존재하지 않습니다. C++ 11에서 표준에는 스레드 지원이 포함되어 있으며 이것은 블록 범위 정적 변수 (6.7/4)의 초기화 설명에 추가되었습니다. "변수가 초기화되는 동안 컨트롤이 동시에 선언에 들어가면 동시 실행 초기화 완료를 기다려야한다. " –

+1

답변을 수락하고 10 표를 얻었으므로 답변을 수정 해주십시오. 또한이 질문은 정적 변수의 초기화 주제와 함께 가장 많이 볼 수 있습니다. –

2

공통 구현으로 초기화 된 플래그를 포함하여 모든 것이 중요합니다. 이것은 기본적으로 정적 locals의 초기화가 스레드로부터 안전하지 않은 이유와 pthread_once가 존재하는 이유입니다.

한 약간의주의 : 컴파일러가 정적 지역 변수가 사용되는 처음 구성되어 "처럼 행동"코드를 방출해야합니다. 정수 초기화는 부작용이 없으므로 (사용자 코드를 호출하지 않기 때문에) int를 초기화 할 때 컴파일러가 결정합니다. 사용자 코드는 그것이하는 일을 "합법적으로"알 수 없습니다.

분명히 당신은 어셈블리 코드를 보면, 또는 정의되지 않은 동작을 자극하고 실제로 일어나는에서 공제 할 수 있습니다. 그러나 C++ 표준은 그 동작이 "스펙처럼 말하는 것"이 ​​아니라고 주장하는 유효한 근거로 간주하지 않습니다.

1

처음으로 함수가 호출 될 때까지 초기화되지 않는다는 것을 알고 있습니다. 컴파일러는 함수가 처음 호출 될 때를 알 수있는 방법이 없으므로 어떻게이 동작을 생성합니까? 그것은 본질적으로 함수 본문에 if 블록을 도입합니까?

그렇습니다. FWIW는 스레드 안전성이 반드시 필요한 것은 아닙니다 (두 스레드가 동시에 "처음으로"라고 말한 경우).

그런 이유로 변수가 전역 범위 (함수 나 클래스 또는 네임 스페이스 또는 외부 연결이없는 정적 임)에서 정의하는 것이 더 좋을 수 있으므로 프로그램이 시작되기 전에 초기화되지 않고 실행되지 않고 초기화됩니다. 시간 ""만약.

10

This question은 유사하지만 실 안전성에 대해서는 언급하지 않았다. C++ 0x는 함수의 정적 초기화를 스레드로부터 안전하게 해줍니다.

합니다 (C++0x FCD 함수 정역학 6.7/4 참조 : ". 제어 변수가 초기화되고 동시에 상태 선언을 입력하면, 동시 실행이 초기화 완료를 기다려야한다")

한 다른 언급되지 않은 것은 기능 정적이 구조의 역순으로 파괴된다는 것입니다. 따라서 컴파일러는 종료시에 호출 할 소멸자 목록을 유지합니다 (atexit과 동일한 목록 일 수도 있고 그렇지 않을 수도 있습니다).

+2

C++ 0x에서 스레드 안전성에 대한 참조/인용문을 제공 할 수 있습니까? 나는 하나도 찾지 못했다. – ChrisW

1

run-before-main() 코드 (cinit/무엇이든)는 사전 초기화 된 데이터 (정적 및 비 정적 모두)를 const 데이터 세그먼트에서 ram으로 복사 할 수있는 임베디드 코드입니다. ROM에. 이는 코드가 다시로드 될 수있는 일종의 백업 스토어 (디스크)에서 코드가 실행되지 않는 경우에 유용합니다. 다시 이것은 main() 전에 수행되기 때문에 언어 요구 사항을 위반하지 않습니다.

가벼운 탄젠트 : 이맥스 밖에서 많은 일을하지는 못했지만 프로그램이나 컴파일러는 기본적으로 프로세스에서 코드를 실행하고 객체를 인스턴스화/초기화 한 다음 프로세스를 중단하고 덤프 할 수 있습니다. 이맥스 (Emacs)는 많은 양의 elisp (즉, 씹어 라)를로드하고 실행 가능한 상태로 실행 상태를 덤프하여 각 호출에 대해 파싱하는 비용을 피하기 위해 이와 비슷한 작업을 수행합니다.

관련 문제