背景
某做硬件的朋友遇到一问题, 在他们项目中使用了初始化为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段
, 进而导致文件体积变大.