3. 포인터 - "C 프로그램" 실행과 역참조
* 일반적으로 메모리 모델을 다이어그램으로 그릴 때, 낮은 주소를 아래에, 높은 주소를 위에 둔다.
한국은 반대로 낮은 주소를 위에 두고, 스텍을 아래에서 위로 쌓는 그림을 사용하기 때문에, 자료 조사할 때 혼동할 수 있다.
결국 스텍은 차례대로 쌓는 이미지를 기억하고 접근해야 한다.
1. C언어의 메모리 모델(Memory Layout of C Programs)
* 빌드와 실행을 이해했다고 가정한다.
1.1 메모리 동작
1.2 컴퓨터가 프로그램을 실행하는 관점에서, 개발자 소스코드의 변수 이름이 필요할까?
빌드
컴퓨터는 프로그래밍 언어(소스 코드)가 필요 없다.
C언어는 사람(개발자)가 필요한 언어다.
실행
컴퓨터는, 0과 1 그리고 저장된 공간을 식별할 고유 메모리 주소가 필요하다.
포인터는, 컴퓨터가 아닌 사람이 프로그래밍 언어(소스코드)에서 고유 메모리 주소를 사용하는 방법이다.
그렇다면, 왜 컴퓨터가 아닌 사람이 프로그래밍 언어(소스코드)에서 고유 메모리 주소를 사용 할 필요가 있는가?


1.3 C언어의 메모리 모델 (1)

1.3.1 코드(Code) 영역
메모리의 코드(Code) 영역은 실행할 프로그램의 코드가 저장되는 영역으로 텍스트 영역이라고도 부른다. Hex 파일이나 BIN 파일 메모리이다. CPU는 코드 영역에 저장된 명령어를 하나씩 가져가서 처리하게 되며 컴파일 타임에 결정되고 중간에 코드를 바꿀 수 없게 Read-Only로 지정되어 있다.
1.3.2 데이터(Data) 영역
메모리의 데이터(Data) 영역은 프로그램의 전역 변수(global), 정적(static) 변수, 배열(array), 구조체(structure)가 저장되는 영역이다. 초기화된 데이터는 data영역에 저장되며, 초기화되지 않는 데이터는 BSS(Block Stated Symbol) 영역에 저장된다. 이 영역은 실행 도중에 전역 변수가 변경될 수도 있으니 Read-Write로 지정되어 있다. 프로그램의 시작과 함께 할당되고, 프로그램 종료 시 소멸된다.
1.3.3 힙(Heap) 영역
메모리의 힙(Heap) 영역은 사용자가 집접 관리할 수 있는 '그기고 해야만 하는' 메모리 영역으로 사용자에 의해 메모리 공간이 동적으로 할당되고 해제된다. 힙 영역은 메모리의 낮은 주소에서 높은 주소의 방향으로 할당된다.
1.3.4. 스택(Stack) 영역
메모리의 스택(Stack) 영역은 함수의 호출과 관계되는 지역 변수와 매개변수가 저장되는 영역으로 함수의 호출과 함께 할당되며, 함수의 호출이 완료되면 소멸된다. 이렇게 스택영역에 저장되는 함수의 호출 정보를 스택 프레임이라고 한다. 스택 영역은 푸시(push) 동작으로 데이터를 저장하고 , 팝(pop) 동작으로 데이터를 인출한다. 이러한 스택은 후입선출(LIFO, Last-In First-Out) 방식에 따라 동작하므로, 가장 늦게 저장된 데이터가 가장 먼저 인출된다. 스택 영역은 메모리의 높은 주소에서 낮은 주소의 방향으로 할당된다.
1.3.5 추가 예시

1.4 C언어의 메모리 모델 (2) - 스텍(Stack)
#include <stdio.h>
int b()
{
printf("b\n");
return 0;
}
int c()
{
printf("c\n");
return 0;
}
int a()
{
printf("a\n");
b();
c();
printf("a_end\n");
return 0;
}
int main()
{
printf("main\n");
a();
return 0;
}

정리
- 스텍에 호출된 함수 프레임은 다른 스텍 프레임의 지역 변수를 침범 할 수 없다.
- 메모리 주소를 통해 다른 프레임의 지역 변수 데이터를 조작하는 방법이 유일하다.
- 지역 변수와 전역 변수를 구분짓는 이유이기도 하다.
- 학습자가 전역변수를 사용하면, 이러한 개념을 익힐 수 없기에 금지시킨다.
2. C언어 함수와 역참조
예시 - 두 값 교환
#include <stdio.h>
void swap1(int a, int b);
void swap2(int *a, int *b);
int main(void)
{
int a = 10, b = 20;
int temp;
printf("0. a:%d, b:%d\n", a, b);
temp = a; // temp = 10, a = 10, b = 20
a = b; // temp = 10, a = 20, b = 20
b = temp; // temp = 10, a = 20, c = 10
// swap1(a, b);
// printf("1. a:%d, b:%d\n", a, b);
// swap2(&a, &b);
// printf("2. a:%d, b:%d\n", a, b);
return 0;
}
void swap1(int a, int b)
{
int temp;
temp = a; // temp = 10, a = 10, b = 20
a = b; // temp = 10, a = 20, b = 20
b = temp; // temp = 10, a = 20, c = 10
}
void swap2(int *a, int *b)
{
int temp;
temp = *a; // temp = 10, a = 10, b = 20
*a = *b; // temp = 10, a = 20, b = 20
*b = temp; // temp = 10, a = 20, c = 10
}