본문 바로가기

무언가 만들기 위한 지식/C,C++,Embedded C

[All] 기억부류지정자 Extern, Static, Auto, Register


예전 필자 블로그에서 메모리맵(Memory Map)에 대해서 언급한 적이 있다.
Embedded 프로그래밍에서는 여러 지정자들을 메모리맵(로딩되는 세그먼트)과 연관지어 생각해야 한다.

기억 부류 지정자에는 Extern/Static/Auto/Register가 있다.
위 지정자들에 의해서 데이터가 저장될 메모리의 영역이 결정된다. 결정된 메모리 영역에 따라 데이터 초기화나 유효범위,시간이 달라진다.

데이터의 크기를 결정짓는 지정자는 void/char/short/int/long/float/double/signed/unsigned가 있고,
메모리의 성질 변화를 일으키는 타입한정자로는 const/volatile이 있다. (const는 예전 포스팅, volatile은 포스팅 예정)

이번 포스팅에서는 기억부류 지정다 4가지에 대해 알아볼 것이고, 이것은 할당되는 메모리영역과 관련이 있다.

시작하기 전에 예전 포스팅에서 사용하였던, Memory Map을 다시 상기해보자.


메모리 맵은 위처럼 구성되고 text/data/bss 영역에 관한 설명은 생략하겠다.
아래 링크를 참조하면 도움이 될 것이다.  archive


위 그림 메모리 맵에 할당되는 C언어의 자료형 타입선언을 정리해보면 다음과 같다.

문자열과 상수   Text Segment (RO-DATA)
초기화되지 전역변수와 정적변수  → DATA Segment
초기화되지 않은 전역변수와 정적변수    BSS Segment 
지역변수와 함수인자   Stack 
레지스터변수    CPU Register 

위 내용을 정리하면 Memory Map과 매치시켜보면서 기억부류 지정자들에 대해알아보자.

1. static

static으로 선언된 변수는 Data Segment(명확히 RW-DATA)에 저장된다.(초기화 되지 않으면 BSS) 프로그램이 실행될때 생성되며, 프로그램이 수명이 다할때 까지 유지된다. Data Segment에는 또한 전역변수도 같이 저장된다.
이는 소스내 어디에서나 사용가능하도록 메모리 공간을 할당해주는 의미이다. 로딩시 XIP(Excute In Place)구조일 경우 Stack이 아닌 공간에 일정 고정된 주소를 할당해 주는 것이다.
전역변수(Global Variable)와 정적변수(Static)는 같은 DATA 영역에 할당되며, 공통점도 많다.
Static 선언자는 크게 두가지 사용으로 나뉜다.
첫번째, Static Local Variable(정적 지역변수)
정적 지역변수는 함수내에서 Static을 선언하는 경우이다.
이 때의 특징은,
- 영역은 Data영역에 포함되어 프로그램 종료시까지 존재한다. (일반 Local변수처럼 Stack이 아니다.)
- 그러나 다른 함수에서 지정된 Static Local Variable Name(즉, 변수명)으로 접근이 불가능하다.
- 접근을 하고 싶다면 함수 자체에서 Static 변수의 주소를 리턴하여 주소로 접근해야 한다. 
  단순한 변수명으로는 절대 접근 할 수 없다.
※ 그렇기 때문에 현재 코드상에서 어디서나 접근하기 위해서 함수내에서 static을 선언한후, 사용하여 프로그래밍을 설계하면 큰 실수가 된다.


15 Line에서 보면 잘못된 접근으로 Error가 난 모습을 볼 수 있다.
제대로 접근하려면 2~6 Line처럼 선언된 정적 지역변수의 주소를 리턴해서 사용해주어야 한다.

두번째, Static Global Variable(정적 전역변수)

정적 전역변수로 선언을 하게되면, 일반 전역변수처럼 현재 코드 전체에서 사용이 가능하다.
하지만 일반전역변수와의 차이점은
전역변수는 extern 키워드를 이용하여 외부파일에서 접근이 가능하나, 정적변수는 접근이 불가능한 것이다.
즉, 범위에 있어차이가 있다.
static 전역변수는 외부파일에서는 보이지 않기 때문에 외부에서 선언된 같은 이름의 전역변수와 충돌하지 않는다.
그렇기 때문에 전역변수가 외부파일에 영향을 받지 않게 하고 싶을때 사용된다.

2. Extern

전역변수는 함수 외부에서 선언되고, Data Segment에 저장이 된다. 정적변수와 마찬가지로 프로그램이 끝날때까지 일정 영역에 자리잡고 있다.변수중 유효범위가 가장 넓은 변수로 현재 코드에서만이 아니라 링크되는 모든 프로그램에서 사용하는 변수이다.
이 Extern 키워드에는 두가지 의미가 있는데,
하나는 전역변수를 의미하는 키워드와 다른 모듈의 전역변수를 사용하겠다는 의미이다.
(다른 소스에 선언되어 있다고 정하는 것. - 이렇게 지정해 놓으면 마치 현재 코드에서 선언해놓은 것처럼 사용이 가능하다.)
보통 전역변수를 의미하기위해서는 auto변수처럼 생략하여 사용한다.
그렇기 때문에 보통 extern은 외부에 선언되어 있는 함수 또는 변수를 현재 소스에 알려주기 위해 사용된다.
(그렇게 해야 컴파일러가 인식을하고 컴파일을 진행한다. - 즉 현재 코드에서 선언되지 않은 외부 파일상의 변수를 사용하기 위함. ※ 단언급한것처럼 외부파일에 있는 static이나 로컬변수는 사용못한다. 외부 파일에 있는 전역변수만 가져다 사용이 가능하다.)
이러한 외부변수 선언은  링커에 의해서 링킹(linking)이 이루어지면, 오브젝트 파일들이 합쳐져서 사용이 가능해진다.
Extern은 외부파일에 있는 전역변수를 현재 모듈에서 사용할 수 있도록 컴파일러에게 알려주는 역할 을 한다.

여러 C소스들을 합쳐 사용하여 컴파일 하는 프로젝트에서 Extern은 꽤나 많이 쓰인다.
특히 ARM 어셈블러와 C언어 상에서 함수를 서로 가져다가 사용할경우나 각 .c 파일간에 다른 파일의 함수를 가져다 쓰기 위해서는 많이 사용된다.
extern만 정의되어 있는 헤더파일을 따로 만들어서 include한후, 사용하기도 한다.

3. Auto

자동변수 또는 지역변수라고하며, Stack에 저장이 된다. 특정 블록이 실행된때 메모리를 할당받고, 블록이 끝날때 생명이 다한다. (Stack의 특징). 보통 블록안에서 Auto라는 지정자는 생략하여 사용된다.
간단하게 생명주기는 해당 블록안에만 있다고 생각하면 된다.

4. Register

이 변수를 선언하면, 메모리가 아닌 Register에 할당이 된다. (Register의 개념은 알고 있다고 전제)
=> Register는 Rom이나 Ram과는 다른 CPU내부의 가장 가까이있는 기억장치이다. 그래서 가장 고속이나, 메모리 크기가 작다.
C언어상에 Register를 선언하면 메모리가 아닌 Register에 저장되기 때문에 속도를 높일 수 있으나,
선언했다고 무조건 Register에 할당하는 것은 아니다. 그것은 현재 레지스터의 사용상황에 따라 공간이 존재할때만 할당한다.
빈 레지스터가 없다면 그냥 스택에 할당된다. (함수내부에서만 선언이 가능)