내일 코어타임을 위해 포인터에 대해서 알아보았당
Pointer
- address : the location of the memeory, where that memory lives
- value : the data stored at that location, what memory lives there
- pointer : the value of the address. It is pointing that location.

int* px = &x; //px라는 정수형 포인터는 변수 x의 주소를 가리킴
int y = *px; //y값은 px라는 포인터가 가리키는 값
*가 타입과 함께 쓰일 때는 해당 타입값을 가리키는 포인터
*가 타입과 함께 쓰이지 않을 때는 역참조 (go and grab that value)
why do we use the pointer?
(the reason that pointers are inevitable when coding in c)
포인터를 왜 쓰는지 알아보기 전에 먼저 프로그램이 실행될 때 메모리에 어떻게 적재되는지 알아보자.
프로그램이 실행될 때 사용되는 (가상)메모리 구조는 다음과 같다 :

- Text/Code : 컴파일된 기계어 코드 (read only / static memory)
- Data : 전역변수 중 초기화된 것들 (global memory / static memory)
- Heap : malloc, new로 할당되는 메모리. 런타임에 커짐 (dynamic memory)
- Stack : 함수 호출 시 지역변수, 리턴 주소, 매개 변수 (콜스택) (dynamic memory)
그리고 함수가 호출되면 스택 안에 다음과 같은 데이터가 생성됨 :
- Call Stack
- 프로그램에서 함수가 호출될 때마다 관련 정보를 저장하는 스택 구조의 메모리 공간
- 함수가 끝나면 해당 함수에 대한 정보가 스택에서 pop되어 사라짐
- Activation Record == Stack Frame
- 하나의 함수 호출에 대한 정보(로컬 변수, 매개변수, 반환 주소 등)를 저장하는 메모리 블록
- 함수가 호출될 때마다 콜 스택에 push되는 요소
예제 :
int globalVar = 10; // Data 영역
int main() {
int a = 5; // Stack (main 함수의 지역 변수이므로 main함수의 Activation Record에 있음)
int *p = malloc(sizeof(int)); // Heap
*p = 20; //Stack (main 함수의 지역 변수이므로 int* 타입 포인터는 Stack의 main 함수의 Activation Record에 있음. 그리고 이 포인터가 가리키는 값은 Heap에 있음)
return 0;
}
- 프로그램이 실행되면 메인 메모리의 Text/Code 영역에 실행할 모든 코드가 적재된다.
- 가장 먼저 main()함수가 호출되고 그 후로 한 줄씩 코드가 실행된다.
- Stack 내부에 main()함수에 해당되는 Activation Record가 하나 생성된다. 그 안에는 main()함수에서 선언하여 할당된 지역 변수들이 존재한다. 즉 int형 변수 a, int*형 변수 p가 선언되고 할당되었으므로 이 변수들이 포함되어있고 리턴 주소 등이 있겠다..
- int* p 변수 자체는 Stack 내부의 Activation Record에 존재하고 Heap에 동적 할당된 공간의 첫 번째 주소가 저장되어있다.
💡Stack에 할당된 값은 프로그램이 직접 접근이 가능하지만 Heap에 할당된 값은 프로그램이 포인터를 통해 간접적으로 접근해야 한다.
이유?
메모리 구조와 CPU의 명령어 동작 방식에 관련된 부분이다.
간단히 말하면 스택은 컴파일 타임에 지역 변수 주소가 고정된 offset값으로 주어져서 바로 접근 가능한 반면, 힙은 주소가 런타임에 동적으로 결정되므로 포인터가 있어야 접근 가능하다.
- Stack은 함수 호출 마다 생성되는 Stack Frame에 지역 변수들이 저장되고, 컴파일 시 Stack Frame의 특정 위치에 해당 지역 변수가 존재한다고 결정된다. 따라서 주소 계산이 고정된 offset으로 이루어진다. 즉 CPU 입장에서 그 지역 변수에 접근할 때 현재 함수의 Stack Frame에서 얼마만큼 떨어진 위치에 있는지를 계산해서 바로 접근 가능하다.
- 반면 Heap은 malloc, new를 통해 런타임에 OS가 메모리를 요청받아 동적으로 할당된다. 즉 Heap에 생성된 공간의 주소가 어딘지 컴파일 시점에 알 수 없다. 실행 시 결정된다. 따라서 Stack Frame의 포인터를 통해 힙 주소를 알아내고 그 주소를 역참조하여 메모리에 접근해야 한다.
힙은 왜 이렇게 복잡하게 접근해야 할까..?
- 프로그램이 실행 중 얼마만큼 쓸지 모르는 메모리를 요청해서 얻는 구조라서 컴파일 시 주소를 알 수 없다.
- 힙은 OS가 메모리 풀에서 빈 공간을 찾아 임의로 주소를 할당해준다.
- 반드시 힙의 주소를 저장하고 있다가(포인터), 그 주소를 역참조해야 실제 데이터에 접근 가능하다.
그래도 굳이 포인터로 힙영역을 역참조해야할 필요가 있나..? 동적으로 힙에 공간이 생성되면 주소가 나오잖아 그럼 그 주소 어딘지 cpu가 계산해서 바로 가면 되잖아
- 스택에 있는 데이터는 cpu가 rbp-4 이런 식으로 접근 가능
- 반면 힙에 있는 데이터는 주소가 0x6000a0 이런 식이고 cpu가 이 주소를 알기 위해서는 프로그램이 직접 포인터를 사용해 이 주소를 저장하고 기억해야 한다.
암튼 얘기를 하다 보니까 포인터가 왜 쓰여야 하는지에 대한 이유가 하나 나왔는데
바로 '힙 영역에 접근하기 위해서'이다.
그리고 또 한가지 이유가 있다.
Call by reference처럼 동작하기 위해서이다.
c언어에서는 모든 함수가 항상 Call by value로 동작한다.
즉 호출된 함수 각자 마다 Stack 내부에 생성된 Activation Record 안에서만 유효한 로직이 실행되고, Activation Record가 pop되면 실행된 로직에 의해 바뀐 지역변수도 함께 사라져버린다.
따라서 특정 함수에서 다른 함수를 호출할 때 변경하고싶은 본인의 지역 변수의 '주소'를 넘겨줌으로써
호출된 함수가 그 주소에 접근하여 값을 변경할 수 있게 한다.
'정글' 카테고리의 다른 글
| 정적 전역 변수 (2) | 2025.04.29 |
|---|---|
| RBTree 정리 (0) | 2025.04.18 |
| Data-Structures Binary Search Tree Q4 - Post Order Iterative 풀이 (2) | 2025.04.17 |
| [정글] c언어 특강 (2) | 2025.04.16 |
| [C언어] 기초 정리(2) - 포인터 (0) | 2025.04.11 |