/

핵심 요약

.data .bss 차이는 초기값에서 갈립니다. .data는 0이 아닌 초기값이 있는 전역·정적 변수가 들어가며 그 초기값이 파일(임베디드에서는 플래시)에 저장됩니다. .bss는 초기값이 없거나 0인 변수가 들어가며 파일에는 크기만 기록되고, 프로그램 시작 시 0으로 채워집니다. 그래서 큰 0 배열을 전역으로 선언해도 실행 파일은 그만큼 커지지 않습니다.

이 글의 수치는 모두 x86-64 Linux 호스트에서 2026-10-23(KST)에 직접 측정한 예시입니다. 도구는 gcc (Debian 14.2.0-19) 14.2.0, GNU size (GNU Binutils for Debian) 2.44입니다. 섹션 크기는 아키텍처, 컴파일러, 옵션에 따라 달라질 수 있습니다.

.data .bss 차이는 무엇입니까?

한 줄 답: .data는 0이 아닌 초기값이 있는 전역·정적 변수, .bss는 초기값이 없거나 0인 전역·정적 변수가 놓이는 섹션입니다.

.data .bss 차이를 이해하려면 먼저 프로그램의 메모리 섹션 전체를 살펴보는 것이 좋습니다. 이 글에서 다루는 영역은 다음과 같습니다.

  • .text: 실행 코드(함수)
  • .rodata: 읽기 전용 데이터(const 전역 등)
  • .data: 0이 아닌 초기값이 있는 전역·정적 변수
  • .bss: 초기값이 없거나 0인 전역·정적 변수
  • 힙: 실행 중 동적으로 할당하는 영역(위쪽으로 자람)
  • 스택: 지역 변수와 함수 호출 정보(아래쪽으로 자람)

변수마다 어느 섹션에 들어갑니까?

한 줄 답: 초기값이 0이 아니면 .data, 없거나 0이면 .bss, const 전역은 이 x86-64 환경에서는 .rodata, 지역 변수는 스택입니다.

아래 sections.c로 확인했습니다. 주석에 변수별로 기대하는 섹션을 적었습니다.

int initialized_global = 42;      /* .data */
int zero_global;                  /* .bss */
static int zero_static = 0;       /* .bss */
const int read_only = 7;          /* .rodata */
int big_buffer[1024];             /* .bss : 파일 크기에 안 들어감 */
int main(void) {
    int local = 1;                /* 스택 */
    return initialized_global + zero_global + zero_static + read_only + big_buffer[0] + local;
}

gcc -O0 -c sections.c && size sections.o의 출력입니다. 이 x86-64 환경에서는 text 115, data 4, bss 4132로 나왔습니다.

   text	   data	    bss	    dec	    hex	filename
    115	      4	   4132	   4251	   109b	sections.o

size -A sections.o로 섹션별 크기를 보면 다음과 같습니다.

sections.o  :
section           size   addr
.text               55      0
.data                4      0
.bss              4132      0
.rodata              4      0
.comment            32      0
.note.GNU-stack      0      0
.eh_frame           56      0
Total             4283

objdump -t sections.o에서 관련 줄만 발췌했습니다.

sections.o:     file format elf64-x86-64

SYMBOL TABLE:
0000000000001020 l     O .bss	0000000000000004 zero_static
0000000000000000 g     O .data	0000000000000004 initialized_global
0000000000000000 g     O .bss	0000000000000004 zero_global
0000000000000000 g     O .rodata	0000000000000004 read_only
0000000000000020 g     O .bss	0000000000001000 big_buffer
0000000000000000 g     F .text	0000000000000037 main

읽는 방법은 다음과 같습니다. 두 번째 열의 l/g는 로컬/글로벌 바인딩이며 static 변수는 l입니다. O는 객체(변수), F는 함수입니다. 그다음이 섹션 이름, 16진수 크기, 심볼 이름입니다. big_buffer의 0x1000은 4096바이트이며, 이 환경에서 int 4바이트 1024개와 같습니다. 지역 변수 local은 스택에 놓이므로 심볼 표에 나타나지 않습니다.

크로스 툴체인에도 size와 objdump가 함께 들어 있습니다(aarch64 크로스 컴파일 CMake 툴체인). 심볼 주소를 실행 중에 확인하려면 gdb 글을 참고하십시오.

.bss는 왜 파일 크기를 늘리지 않습니까?

한 줄 답: .bss는 크기 정보만 갖고 있고 0 자체는 파일에 저장되지 않기 때문입니다.

같은 코드에서 big_buffer만 바꿔 실행 파일 두 개를 만들었습니다. sections_big은 int big_buffer[1024];, sections_small(sections_small.c)은 int big_buffer[1];만 다르고 나머지는 같습니다. 둘 다 이 호스트에서 gcc로 링크했습니다. size sections_big sections_small의 출력입니다.

   text	   data	    bss	    dec	    hex	filename
   1264	    532	   4168	   5964	   174c	sections_big
   1264	    532	     20	   1816	    718	sections_small

stat -c '%s %n'으로 본 파일 크기(바이트)입니다.

16024 sections_big
16032 sections_small

이 x86-64 환경에서는 bss가 4168과 20으로 크게 다르지만, 파일 크기는 약 4 KB 배열만큼 늘지 않았습니다. 이번 실행에서는 sections_big이 오히려 8바이트 작았는데, 이런 몇 바이트 차이는 링커의 섹션 배치·정렬에서 생길 수 있습니다. 핵심은 앞의 size -A sections.o에서 .bss가 4132바이트(.data는 4바이트)로 잡혀 있어도, .bss에는 0이 저장되지 않고 크기만 기록되므로 파일 안에 배열 크기만큼의 공간을 차지하지 않는다는 점입니다. 또 text 1264와 data 532에는 우리 변수뿐 아니라 C 런타임 시작 코드도 포함되어 있습니다.

임베디드에서는 시작할 때 무슨 일이 일어납니까?

한 줄 답: 시작 코드가 플래시의 .data 초기값을 RAM으로 복사하고 .bss를 0으로 채운 뒤 main을 호출합니다.

일반적인 임베디드 구성에서 .text, .rodata, 그리고 .data의 초기값은 플래시(비휘발성 메모리)에 있습니다. main이 실행되기 전에 시작 코드가 .data의 초기값을 플래시에서 RAM으로 복사하고, .bss 영역을 0으로 채우고, 스택을 설정한 다음 main을 호출합니다. 이 배치는 링커 스크립트가 정하며, 여기서는 그 존재만 언급합니다. 호스트 Linux에서는 로더와 커널이 파일에서 .data를 매핑하고, .bss에는 0으로 채워진 페이지를 제공합니다.

부팅 과정 전체는 임베디드 리눅스 부팅 과정, 부트로더부터 유저랜드까지에서, 빌드 순서는 크로스 컴파일이란? ARM용 gcc로 빌드하고 QEMU에서 실행할 때 순서에서 다룹니다.

면접에서는 어떻게 답하면 됩니까?

한 줄 답: "0이 아닌 초기값이 있으면 .data, 없거나 0이면 .bss, 파일에는 .bss의 크기만 기록된다"로 시작하고 근거로 size 출력을 덧붙이면 됩니다.

답변 틀은 다음과 같습니다.

  1. 0이 아닌 초기값이 있는 전역·정적 변수는 .data에 들어가고, 그 초기값은 파일(임베디드에서는 플래시)에 저장됩니다.
  2. 초기값이 없거나 0인 전역·정적 변수는 .bss에 들어가며, 파일에는 크기만 기록됩니다.
  3. 시작 코드(또는 로더)가 .data를 초기화하고 .bss를 0으로 채운 뒤 main이 실행됩니다.
  4. size 명령으로 text, data, bss를 확인할 수 있고, 큰 전역 0 배열은 bss만 키우며 파일 크기는 거의 늘리지 않습니다.

자주 묻는 질문은 무엇입니까?

한 줄 답: 변수의 초기값과 종류에 따라 들어가는 섹션이 달라지며, 아래에 자주 나오는 네 가지를 정리했습니다.
static 지역 변수는 어디에 들어갑니까?

초기값에 따라 다릅니다. 0이 아닌 초기값이 있으면 .data, 없거나 0이면 .bss에 들어갑니다.

const 전역 변수는 어디에 들어갑니까?

이 x86-64 환경의 GCC에서는 .rodata에 들어갔습니다(read_only). 툴체인에 따라 다를 수 있습니다.

0으로 초기화한 전역 변수는 .data입니까 .bss입니까?

GCC에서는 .bss입니다. 위 출력의 zero_static이 근거입니다.

스택 넘침과는 어떤 관계입니까?

스택과 힙은 이 섹션들에 포함되지 않습니다. 큰 지역 배열은 스택을 사용하고, 큰 전역 0 배열은 .bss를 사용합니다.

함께 보면 좋은 글은 무엇입니까?

한 줄 답: 크로스 툴체인, gdb, 크로스 컴파일, 임베디드 리눅스 부팅 과정 글을 함께 보시면 좋습니다.

출처는 어디입니까?

한 줄 답: GNU ld 매뉴얼과 GNU binutils의 size, objdump 문서, 그리고 직접 측정한 결과입니다.

+ Recent posts