Back to Top

初始化为0的数组存放在哪儿?

背景

某做硬件的朋友遇到一问题, 在他们项目中使用了初始化为0的全局数组, 它导致程序文件过大, 无法写入芯片中.

他们想改为指针, 但因为是多维数组, 要调整的地方挺多, 可能会引入新的风险.

方案

我建议他使用未初始化的数组, 因为未初始化的变量放在.bss段, 初始化的放在.data段.

.bss段通常用来存放程序中未初始化的或者初始化为0的全局变量和静态变量的, 可读写,在程序执行之前BSS段会自动清0。 .bss段只是为未初始化的全局变量和局部静态变量预留位置, 并不占用文件空间.

于是我做了个示例, 想向他演示下, 结果并非我的预想:

/*file: test.c */
/*gcc test.c */
#include <stdio.h>

char* sz = NULL;  /*编译后文件大小 8531*/
char sz[1024 * 1024 * 16]; /*编译后文件大小 8531*/
char sz[1024 * 1024 * 16] = {0}; /*编译后文件大小 8531*/
char sz[1024 * 1024 * 16] = {1}; /*编译后文件大小 16785795*/ 

int main(void)
{
    printf("%s\n", sz); 
    return 0;
}
类型 指针 数组未初始化 数组初始化为0 数组初始化为非0
大小 8531 8531 8531 16785795

而朋友的项目中, 初始化为0导致了程序过大, 但在我的示例中, 初始化为0并没有变大.

分析

使用objdump -t查看符号表, 发现朋友初始化0的数组存放在.data段, 但对于我的编译结果查看:

objdump -t a_small | grep sz
0000000000601060 g     O .bss   0000000001000000              sz

objdump -t a_large | grep sz
0000000000601060 g     O .data  0000000001000000              sz

初始化为0的数组存放在.bss段, 初始化为非0的数组存放在.data段.

通过搜索发现gcc有-fno-zero-initialized-in-bss的编译选项, 增加该编译选项后, sz初始化为0时, 也同样存储在.data段了.

结论

朋友的项目因为启用了-fno-zero-initialized-in-bss编译选项, 导致初始化为0的全局数组会存放在.data段, 进而导致文件体积变大.