메모리 영역

KWANHO PARK·2025년 10월 20일
	 High Address ▲
      ┌───────────┐
      │          │
      │   Stack  │
      │          │
      ├───────────┤
      │          │
      │  (Heap)  │
      │          │
      ├───────────┤
      │   BSS    │
      ├───────────┤
      │   Data   │
      ├───────────┤
      │   Code   │
      │  (Text)  │
      └───────────┘
     Low Address  ▼

1. 코드 (Code / Text) 영역

코드 영역 : 실행할 프로그램의 기계어 명령어들이 저장되는 공간

  • CPU는 이 영역에 있는 명령어들을 순서대로 읽어서 프로그램 실행. - 이 영역은 프로그램이 실행되는 동안 변하지 않아야 하므로, 보통 읽기 전용(Read-Only)으로 설정되어 실수로 코드가 변경되는 것 방지
  • 저장 대상: main(), func() 등 함수들의 기계어 코드.

2. 데이터 (Data) 영역

데이터 영역 : 프로그램이 시작될 때부터 끝날 때까지 계속 유지되는 초기값이 있는 전역 변수와 정적(static) 변수가 저장되는 공간

  • 이 변수들의 초기값은 실행 파일(.exe) 자체에 저장되어 있다가, 프로그램이 실행될 때 메모리로 그대로 복사됨
  • 저장 대상: int global_var = 100;, static int s_var = 20;

3. BSS (Block Started by Symbol) 영역

BSS 영역 : 데이터 영역과 마찬가지로 프로그램 전체에서 사용되는 전역 변수와 정적 변수가 저장되지만, 초기값이 없는 변수들이 저장됨

  • 여기 있는 변수들은 프로그램이 시작될 때 운영체제가 자동으로 모두 0으로 초기화해 줌
  • 저장 대상: int global_var;, static int s_var; 등.

4. 힙 (Heap) 영역

힙 영역 : 프로그램이 실행되는 도중에 개발자가 필요에 따라 동적으로 메모리를 할당하고 해제하는 공간

malloc() 함수를 사용해 원하는 크기의 메모리를 빌리고, free()로 반납합니다. 개발자가 직접 관리해야 하므로, 메모리 누수(memory leak)가 발생할 수 있어 주의 필요

  • 저장 대상: 동적으로 할당된 데이터 (예: int* arr = malloc(10 * sizeof(int)); 에서 arr이 가리키는 공간).

5. 스택 (Stack) 영역

스택 영역 : 함수가 호출될 때마다 생성되는 지역 변수, 매개변수, 함수가 끝나고 돌아갈 주소 등이 저장되는 임시 공간

함수가 호출되면 스택에 해당 함수의 공간(스택 프레임)이 쌓이고, 함수 동작이 끝나면 사라짐. LIFO(Last-In First-Out) 구조. 컴파일러가 크기를 자동으로 관리해주므로 매우 빠르고 안전

  • 저장 대상: 함수 내에 선언된 모든 지역 변수 (int local_var = 10;), 함수로 전달되는 인자.

각 데이터영역을 눈으로 확인해보자

#include <stdio.h>

int showCode();
int func();
int func1();

int dataVar1 = 10;
int dataVar2 = 20;
int dataVar3 = 30;

int bssVar1;
int bssVar2;
int bssVar3;

int main(void)
{
	int stackVar1 = 1;
	int stackVar2 = 2;
	int stackVar3 = 3;
	
	printf("Stack Area\n");
	printf("stackVar1 : %p\n", &stackVar1);
	printf("stackVar2 : %p\n", &stackVar2);
	printf("stackVar3 : %p\n", &stackVar3);
	printf("\n");
	printf("Data Area\n");
	printf("dataVar1 : %p\n", &dataVar1);
	printf("dataVar2 : %p\n", &dataVar2);
	printf("dataVar3 : %p\n", &dataVar3);
	printf("\n");
	printf("BSS Area\n");
	printf("bssVar1 : %p\n", &bssVar1);
	printf("bssVar2 : %p\n", &bssVar2);
	printf("bssVar3 : %p\n", &bssVar3);
	printf("\n");
	printf("Code Area\n");
	showCode();	
	
	return 0;
}

int showCode()
{
	printf("func() : %p\n", func);
	printf("func1() : %p\n", func1);
	printf("printf() : %p\n", printf);

	return 0;
}

int func()
{
	
	return 0;
}

int func1()
{
	
	return 0;
}
  • 결과
C:\Users\User\Desktop\Test>memoryArea.exe
Stack Area
stackVar1 : 0019FF28
stackVar2 : 0019FF24
stackVar3 : 0019FF20

Data Area
dataVar1 : 0041C000
dataVar2 : 0041C004
dataVar3 : 0041C008

BSS Area
bssVar1 : 0041D418
bssVar2 : 0041D414
bssVar3 : 0041D410

Code Area
func() : 00401040
func1() : 00401050
printf() : 004011C0
  • 영역별로 각각 자신의 데이터타입 크기만큼 할당된 공간들이 모여있는 것을 확인할 수 있다.(함수는 코드 크기만큼 할당되는데, func()는 16byte가 할당된 걸 확인할 수 있다)

궁금증

왜 Data영역과 BSS영역을 분리해놨을까?

이유 : 최적화를 위해

C언어 표준에 초기화되지 않은 전역/정적 변수는 프로그램 시작 시 반드시 0이 되어야한다고 명시되어 있음

  • 만약 BSS영역이 없다면, 메모리에 들어갈 값이 없는 전역/정적 변수 데이터까지 디스크에 있는 실행파일로부터 읽어들여 메모리에 올려야 함(오버헤드 증가)
  • 이를 막기 위해 초기화되지 않은 전역/정적 변수들은 BSS영역에 배치하고 CPU에서 일괄적으로 해당 변수들에 0을 할당함(디스크 읽는 작업보다 CPU-메모리간 작업이 압도적으로 빠름)

즉, 데이터영역과 BSS영역을 분리한 덕분에 최적화가 이루어짐

  • 실행 파일 크기 감소
  • 프로그램 로드 시간 감소

0개의 댓글