기억영역 클래스 지정자 (Storage Class Specifiers)
스토리지 클래스는 변수의 다양한 특성을 결정하며,
C++에서 지원하는 스토리지 클래스는 다음과 같다.
스토리지 클래스가 결정하는 변수의 특성 :
- 변수의 범위
- 생명주기
- 링크특성
- 외부링크 : 외부소스의 변수를 참조.
- 내부링크 : 내부소스의 변수를 참조.
C++ 기억공간 클래스 :
auto(untill C++11, 키워드의 의미가 변경됨)register(untill C++11, 폐기됨)- static
- extern
- thread_local (since C++11)
- mutable
Auto 스토리지
- C++11 이전
일반적으로 사용되는 변수의 스토리지 타입이다.
스택에 쌓이며, 범위를 벗어나면 스택에서 제거되어 사라진다.
변수의 생명주기가 자동적으로 관리된다는 뜻에서 자동변수라는 이름이 붙었다.
/* C++ 11 이전 */ auto int a = 3; // OK, auto는 기억공간 지정자이다.
C++11 이후
리터럴의 자료형을 분석하여, 변수의 자료형을 추론하는 기능이 추가되었고. (auto data type, Since C++11)
auto의 역할이 위의 기능으로 대체되었으며, 스토리지 지정자의 기능을 잃었다.
/* C++ 11 이후 */ auto int a = 3; // ERROR, auto는 더 이상 기억공간 지정자가 아니다. auto b = 3; // OK, auto는 리터럴의 자료형을 추론한다. int b = 3; 과 같다.
기능이 변경된 것 뿐이지, 그 특성은 여전히 쓰이고 있다.
지금도 명시자를 지정하지 않으면, 자동변수으로 취급한다.
Register 스토리지
- C++11 이전
변수를 RAM이 아니라, 가능한 Register에 저장하도록 요청한다.
레지스터에 여유가 없으면 이 요청은 무시될 수 있다.
컴퓨터에 얼마없는 레지스터를 사용하기 때문에, 전역변수에는 사용할 수 없고.
레지스터에 저장되기 때문에 주소값을 가져올 수 없다.
레지스터 변수에 주소값 취득을 허용하는 컴파일러는,
내부적으로 레지스터 요청을 무시하고, RAM에 저장시켜 주소를 갖도록 한다.
C++17 이전
변수를 레지스터 스토리지로 선언하면 Warning을 발생시키거나,
일부 컴파일러는 요청을 무시하고 RAM에 저장한다.
C++17 이후
변수를 레지스터 스토리지로 선언하면 Compile Error를 발생시킨다.
static 스토리지
변수가 내부링크의 속성으로 선언되도록 한다.
내부링크의 속성 :
같은 소스코드 파일 안에서 같은 이름을 갖는 모든 변수는 하나의 메모리만 사용.
처음으로 선언문을 만났을 때만 선언식을 수행하고, 이후에는 무시한다.
int set_and_get(int v){ static int s_int = v; // 초기화는 변수 생성시, 단 한번만 발생한다. return s_int; } int main() { cout << set_and_get(0) << endl; // print 0 cout << set_and_get(1) << endl; // print 0 cout << set_and_get(2) << endl; // print 0 return 0; }
여러번 함수를 호출하여 s_int의 값을 바꾸려고 했지만,
2행의 초기화는 한번만 발생하고 무시되므로, 첫 값인 0에서 바뀌지 않는다.
올바르게 바꾸면 다음과 같다.
int set_and_get(int v){ static int s_int = 0; // 초기화는 변수 생성시, 단 한번만 발생한다. s_int = v; return s_int; } int main() { cout << set_and_get(0) << endl; // print 0 cout << set_and_get(1) << endl; // print 1 cout << set_and_get(2) << endl; // print 2 return 0; }
extern 스토리지
변수가 외부링크의 속성으로 선언되도록 한다.
외부링크의 속성 :
- 다른 소스코드의 변수를 가져올 수 있음.
- 단, 내부링크(static) 변수는 가져올 수 없음.
/* data.cpp */ int e_i = 1; static int s_i = 2; const int c_i = 3;
/* main1.cpp */ int main(){ extern int e_i; cout << e_i << endl; // print 1; }
/* main2.cpp */ int main(){ extern int e_i; extern int c_i; cout << s_i << endl; // Link Error, 내부링크 변수를 외부링크로 가져올 수 없음. cout << c_i << endl; // Link Error, const도 내부링크 속성. }
Thread_Local 스토리지 (Since C++11)
전역으로 선언될지라도, 각 쓰레드마다 별도의 변수로 적용된다.
extern과 static을 추가적으로 조합하여 링킹할 수 있다.
int v = 0; // 모든 쓰레드가 하나의 v를 사용. void ThreadFunc(int nID) { v++; cout << nID << "th Thread : " << v << endl; } int main() { for(int i=0; i<5; i++) { thread th(ThreadFunc, i); th.join(); } // 0th Thread : 1 // 1th Thread : 2 // 2th Thread : 3 // 3th Thread : 4 // 4th Thread : 5 }
thread_local int v = 0; // 각각의 쓰레드가 v를 생성하여 사용. ... // 0th Thread : 1 // 1th Thread : 1 // 2th Thread : 1 // 3th Thread : 1 // 4th Thread : 1
Mutable 스토리지
클래스 멤버 변수에만 적용할 수 있으며, 멤버 함수의 const 제약을 무시한다.
링크와 아무런 관련없는 스토리지 타입.
또 다른 의미로는,
람다함수의 캡쳐시, 파라미터의 const 제약을 제거하는 것 이다.
class Box{ public: mutable int data = 0; void save(int _data) const { data = _data; // 멤버함수에 const 제약이 있지만 뚫는다. } };
스토리지 스펙 정리
참고문헌
https://psychoria.tistory.com/241
'# Lang > C++' 카테고리의 다른 글
4.2 값의 유형 (glvalue, prvalue, xvalue) (0) | 2019.04.11 |
---|---|
4.1 표현식 (0) | 2019.04.10 |
3.1 CV 제한자 (const, volatile) (0) | 2019.03.31 |
2.3 자료형과 형변환 (0) | 2019.03.29 |
2.2 자료형과 데이터 해석 (0) | 2019.03.27 |