提问人:richardIII 提问时间:11/2/2023 更新时间:11/2/2023 访问量:75
为什么二进制文件的大小不随静态数据的大小而增加?
Why doesn't the size of the binary file increase with the size of the static data?
问:
当我编译以下 C 代码时,gcc -O0
#include <stdlib.h>
#include <stdio.h>
char bytes[1000*1000*1000];
int main(void) {
for (int i = 0; i < 1000*1000; i++)
bytes[i] = (char) i;
printf("%c\n", bytes[56789]);
return EXIT_SUCCESS;
}
二进制文件只有 16kB。 我分配的 1GB 在哪里?它不在堆上,因为我不调用 malloc,也不在堆栈上,因为它太多了。
答:
由于该区域是用零初始化的,因此在某些系统上不需要位于二进制文件中。加载程序将简单地告诉操作系统内存空间的某些部分将被标记分配,操作系统将提供必要的清零页面。
在 x86_64 上的 Linux 上使用 gcc 编译程序 (as ) 后,您可以看到一个“指令”来分配 1,000,000,032 () 字节。 指它不在二进制文件中的事实。a
A
3b9aca20
NOBITS
$ readelf -S a
There are 31 section headers, starting at offset 0x3998:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
...
[26] .bss NOBITS 0000000000004020 00003010
000000003b9aca20 0000000000000000 WA 0 0 32
...
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
它不仅不会占用二进制文件的空间,而且可能占用很少的 RAM。
在具有虚拟内存的系统(例如台式机、平板电脑和手机)上,实际上可以分配一个远大于系统实际 RAM 的零初始化静态数组。在这样的系统上是可能的,因为操作系统只需要在开始修改后开始花费实际资源。即便如此,它可能只需要将资源用于修改的特定内存页(例如 4 KiB 部分)。
您仍然会受到进程地址空间的限制,但这在 64 位系统上几乎是无限的。
定义的数组以数据的形式呈现在二进制中,表示有一个具有一定大小的程序部分,并且要用零初始化。(大小将是累积到此程序部分的所有对象的总和,包括所需的任何填充。当您增加或减少此类数组的大小并重建二进制文件时,唯一需要更改的是该大小的数字。实际上,二进制文件中不需要有直接对应于数组中字节的字节。char bytes[1000*1000*1000];
需要时(例如,当程序尝试使用此程序部分中的数据时),操作系统将为该程序部分(或其中的一部分)提供内存,并确保它以零初始化。
评论
.bss
.bss
.data
char bytes[1000*1000*1000] = { 1 };