핵심 요약
.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 .bss 차이를 이해하려면 먼저 프로그램의 메모리 섹션 전체를 살펴보는 것이 좋습니다. 이 글에서 다루는 영역은 다음과 같습니다.
.text: 실행 코드(함수).rodata: 읽기 전용 데이터(const 전역 등).data: 0이 아닌 초기값이 있는 전역·정적 변수.bss: 초기값이 없거나 0인 전역·정적 변수- 힙: 실행 중 동적으로 할당하는 영역(위쪽으로 자람)
- 스택: 지역 변수와 함수 호출 정보(아래쪽으로 자람)

변수마다 어느 섹션에 들어갑니까?
아래 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는 왜 파일 크기를 늘리지 않습니까?
같은 코드에서 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 런타임 시작 코드도 포함되어 있습니다.
임베디드에서는 시작할 때 무슨 일이 일어납니까?
일반적인 임베디드 구성에서 .text, .rodata, 그리고 .data의 초기값은 플래시(비휘발성 메모리)에 있습니다. main이 실행되기 전에 시작 코드가 .data의 초기값을 플래시에서 RAM으로 복사하고, .bss 영역을 0으로 채우고, 스택을 설정한 다음 main을 호출합니다. 이 배치는 링커 스크립트가 정하며, 여기서는 그 존재만 언급합니다. 호스트 Linux에서는 로더와 커널이 파일에서 .data를 매핑하고, .bss에는 0으로 채워진 페이지를 제공합니다.

부팅 과정 전체는 임베디드 리눅스 부팅 과정, 부트로더부터 유저랜드까지에서, 빌드 순서는 크로스 컴파일이란? ARM용 gcc로 빌드하고 QEMU에서 실행할 때 순서에서 다룹니다.
면접에서는 어떻게 답하면 됩니까?
답변 틀은 다음과 같습니다.
- 0이 아닌 초기값이 있는 전역·정적 변수는 .data에 들어가고, 그 초기값은 파일(임베디드에서는 플래시)에 저장됩니다.
- 초기값이 없거나 0인 전역·정적 변수는 .bss에 들어가며, 파일에는 크기만 기록됩니다.
- 시작 코드(또는 로더)가 .data를 초기화하고 .bss를 0으로 채운 뒤 main이 실행됩니다.
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를 사용합니다.
함께 보면 좋은 글은 무엇입니까?
- aarch64 크로스 컴파일 CMake 툴체인
- gdb
- 크로스 컴파일이란? ARM용 gcc로 빌드하고 QEMU에서 실행할 때 순서
- 임베디드 리눅스 부팅 과정, 부트로더부터 유저랜드까지
출처는 어디입니까?
- GNU ld manual (확인 2026-10-23)
- GNU binutils size (확인 2026-10-23)
- GNU binutils objdump (확인 2026-10-23)
- 직접 측정: 2026-10-23 KST, x86-64 Linux 호스트 (섹션 크기는 아키텍처·컴파일러·옵션에 따라 다릅니다)
Key takeaways
The .data vs .bss difference comes down to initial values. .data holds global and static variables with a non-zero initial value, and those values are stored in the file (in flash on embedded targets). .bss holds variables with no initializer or a zero value; only its size is recorded in the file, and it is zero-filled at startup. That is why a large zero-initialized global array does not make the executable bigger.
All numbers in this post are samples I measured on an x86-64 Linux host on 2026-10-23 (KST), using gcc (Debian 14.2.0-19) 14.2.0 and GNU size (GNU Binutils for Debian) 2.44. Section sizes can differ by architecture, compiler, and options.
What is the .data vs .bss difference?
To understand the .data vs .bss difference, it helps to see the memory sections of a program as a whole. This post covers the following areas.
.text: executable code (functions).rodata: read-only data (such as const globals).data: global and static variables with a non-zero initial value.bss: global and static variables with no initializer or a zero value- Heap: memory allocated dynamically at run time (grows upward)
- Stack: local variables and call frames (grows downward)

Which section does each variable go into?
I checked this with the sections.c below. The comments show the section I expect for each variable.
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;
}
This is the output of gcc -O0 -c sections.c && size sections.o. On this x86-64 host it reports text 115, data 4, and bss 4132. (The Korean text inside the code comments says that big_buffer does not count toward the file size, and that local lives on the stack.)
text data bss dec hex filename
115 4 4132 4251 109b sections.o
Here are the per-section sizes from 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
Relevant lines only, excerpted from 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
How to read it: the second column, l or g, is the local or global binding, and a static variable shows l. O means object (a variable) and F means function. Next come the section name, the size in hex, and the symbol name. The 0x1000 for big_buffer is 4096 bytes, which equals 1024 four-byte ints on this host. The local variable local lives on the stack, so it does not appear in the symbol table.
A cross toolchain also ships size and objdump (aarch64 cross-compile CMake toolchain (Korean)). To check symbol addresses at run time, see the gdb post (Korean).
Why doesn't .bss increase the file size?
I built two executables from the same code that differ only in big_buffer. sections_big uses int big_buffer[1024];, while sections_small (from sections_small.c) uses int big_buffer[1]; and is otherwise identical. Both were linked with gcc on this host. This is the output of 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
File sizes in bytes, from stat -c '%s %n':
16024 sections_big
16032 sections_small
On this x86-64 host, bss differs a lot (4168 vs 20), yet the file did not grow by the roughly 4 KB array. In this run sections_big is even 8 bytes smaller; a difference of a few bytes like this can come from the linker's section placement and alignment. The key point is that even though size -A sections.o above lists .bss at 4132 bytes (.data at 4), .bss records only a size and stores no zeros, so it does not take up array-sized space in the file. Also, the text 1264 and data 532 include C runtime startup code, not just our variables.
What happens at startup on an embedded system?
In a typical embedded setup, .text, .rodata, and the initial values of .data sit in flash (non-volatile memory). Before main runs, startup code copies the .data initial values from flash to RAM, zero-fills the .bss region, sets up the stack, and then calls main. A linker script defines this layout; I only mention it here. On a hosted Linux system, the loader and kernel map .data from the file and provide zero-filled pages for .bss.

The full boot flow is covered in the embedded Linux boot process, from bootloader to userland (Korean), and the build order in what cross-compiling is: building with an ARM gcc and running on QEMU (Korean).
How should I answer this in an interview?
An answer outline:
- Global and static variables with a non-zero initial value go into .data, and those initial values are stored in the file (in flash on embedded targets).
- Global and static variables with no initializer or a zero value go into .bss, and the file records only the size.
- Startup code (or the loader) initializes .data and zero-fills .bss before main runs.
- The
sizecommand shows text, data, and bss; a large global zero array grows only bss and barely changes the file size.
What are the frequently asked questions?
Where does a static local variable go?
It depends on the initializer. A non-zero initial value puts it in .data; no initializer or zero puts it in .bss.
Where does a const global variable go?
With GCC on this x86-64 host it went into .rodata (read_only). It may differ by toolchain.
Is a global initialized to zero in .data or .bss?
With GCC it is .bss. The zero_static entry in the output above is the evidence.
How does this relate to stack overflow?
The stack and heap are not part of these sections. A large local array uses the stack, while a large global zero array uses .bss.
What related posts are worth reading?
- aarch64 cross-compile CMake toolchain (Korean)
- gdb (Korean)
- What cross-compiling is: building with an ARM gcc and running on QEMU (Korean)
- Embedded Linux boot process, from bootloader to userland (Korean)
What are the sources?
- GNU ld manual (checked 2026-10-23)
- GNU binutils size (checked 2026-10-23)
- GNU binutils objdump (checked 2026-10-23)
- Own measurement: 2026-10-23 KST, x86-64 Linux host (section sizes vary with architecture, compiler, and options)
