배열이 전역변수, 지역변수, 포인터로 선언되었을 때의 내부적인 차이

아래의 배열 a, b 와 포인터 c 는 모두 주소를 의미한다.

a, b 는 배열의 첫번째 주소이고, c 는 정수형 포인터이지만 동적할당을 통해 반환받은 첫번째 위치의 주소로써 활용할 수 있다.

int a[5]

int main(void)
{
    int b[5]
    int *c;
}

어쨌든 셋 다 배열로써 볼 수 있지만, 이렇게 전역, 지역으로 선언된 배열과 포인터로 가리키는 동적으로 할당된 배열은 컴파일되는 과정에서 크게 다르다.


전역 변수

전역으로 선언된 변수들은 컴파일 시점에 라벨이 붙고, 데이터 영역에 올라간다.

즉 스택이나 힙에 올라가는 변수가 아니고, a 에 라벨이 붙어서 해당 배열의 값들을 라벨을 기준으로 참조할 수 있게 된다.

라벨이 하나 할당됨으로써 어셈블리 측면에서 볼 때 실행코드 어디서든 라벨을 통해 접근할 수 있어서, 전역으로 선언된 효과를 갖는다.

전역변수인 배열 a 는 컴파일 시점에 대략 ARRAY_A 와 같은 라벨이 부여되고, 실행코드들 중 a 를 참조할 때는 해당 라벨을 붙여줘서 어셈블 과정에서 데이터 영역의 주소를 얻을 수 있다.

특정 함수 호출 안에서만 활성화되는 값들이 아니기 때문에 Activation Record 에 포함되는 값이 아니며, 따라서 스택이 아닌 데이터에 저장된다.


지역 변수

지역 변수들은 해당 함수의 Activation Record 에 포함되면서 스택 영역에 올라간다.

c++ 표준에서는 배열로 선언할 때 크기를 컴파일 시점에서 결정하는데, 이 말은 컴파일 할 때 Activation Record 의 지역 변수들에 해당 공간만큼을 다 할당한다는 뜻이다.

즉 지금처럼 배열 b 를 위한 5개 공간을 포함시켜 스택에 올려놓는다.

현재 활성화된 함수의 기준값이 B 레지스터에 들어가있기 때문에, Activation Record 에서 배열의 원소들에 접근할 수 있는 것이다.


포인터

포인터로 선언된 변수에다가, 런타임 중에 동적으로 배열을 할당 받아서 사용할 경우에는 Activation Record 에 공간이 하나만 할당된다.

배열의 크기를 주어 배열로써 선언했을 때는 A-R 에 배열의 값들이 하나하나 전부 들어가서 참조되었지만, 포인터는 해당 포인터변수를 위한 공간만 주어진다.

따라서 힙 영역에서 동적 할당이 이루어진 이후에, 할당받은 영역의 첫번째 위치만 포인터에 저장시켜놓는 것이다.

포인터 c 는 메인함수의 지역에 선언되었기 때문에 우선은 메인의 A-R 에서 포인터에 접근할 수 있고, 다시 포인터에서 인덱싱을 해서 힙 영역의 원소들에 접근할 수 있다.

물론 포인터 변수 자체가 전역인지 지역인지에 따라 포인터 변수에 접근하는 방식이 달라진다.