通过未初始化全局变量,研究BSS段和COMMON段的不同
2015-08-08 17:13
417 查看
最近正在重温《程序员的自我修养》一书,由于水平比以前有所提升,所以读书的收获也不一样。
下面针对该书3.3.3节BSS段的内容进行更细节的探讨——该节内容不在本文中重复说明了,只说一下结论。对于全局变量来说,如果初始化了不为0的值,那么该全局变量则被保存在data段,如果初始化的值为0,那么将其保存在bss段,如果没有初始化,则将其保存在common段,等到链接时再将其放入到BSS段。关于第三点不同编译器行为会不同,有的编译器会把没有初始化的全局变量直接放到BSS段。
关于上面这个结论,我就不重复进行探讨了,下面探讨一下更细节点的问题。从上面的内容上看,尽管未初始化的全局变量有可能在编译阶段被保存在common段,但是最终还是会放到BSS段。那么我们是否可以将未初始化的全局变量与初始为0的全局变量等同起来呢?
请看下面的代码:
文件test1.c:
#include
int init = 0;
void init1()
{
if (0 == init) {
init = 1;
printf("init1\n");
}
}
编译gcc -g -c test1.c
文件test2.c:
#include
int init = 1;
void init2()
{
if (init) {
init = 0;
printf("init2\n");
}
}
编译gcc -g -c test2.c
文件main.c
void init1();
void init2();
int main()
{
init1();
init2();
return 0;
}
编译gcc -g -o test main.c test1.o test2.o
这时会报错
test2.o:(.data+0x0): multiple
definition of `init'
test1.o:/root/work/test/test1.c:4: first
defined here
collect2: ld returned 1 exit status
这个报错大家都可以接受吧?OK,那么现在我们做一个小小的改动,将文件test1.c中的int init = 0改为int init,对于test1.c来说,这个改动不影响其逻辑,因为init如果未初始化,其值也应该是0。
当前的test1.c
#include <stdio.h>
int init;
void init1()
{
if (0 == init) {
init = 1;
printf("init1\n");
}
}
编译gcc -g -c test1.c
gcc -g -Wall -o test main.c test1.o test2.o
这次即使使用-Wall打开所以warning,也没有任何警告和错误,已经生成了输出文件test。
执行test
[root@Lnx99 test]#./test
init2
好,下面探讨一下为什么是这样。第一种情况,当test1.c中的init被初始化为0时,尽管init被放置在bss段,但是它是一个强符号。而test2.c中,定义了init为1,也是一个强符号,所以引发了错误。第二种情况,当test1.c中的init不进行初始化,尽管其值仍然为0,但是其被保存在common段,为一个弱符号。当test2.c中定义了init为1一个强符号,那么在链接的过程中,gcc会用这个强符号覆盖掉弱符号,并不会引起链接冲突错误。但是在运行阶段,进入init1时,这个init的值却并不是其所期望的值,因此导致没有打印init1。
我想关于BSS与COMMON段的这点不同,应该讲得比较清楚了。从这个区别中,我们需要注意,当定义全局变量时,有两点需要注意:一,如果只有本文件使用,那么需要添加上static;二,即使不能使用static,那么一定要为该全局变量定义初值,即使这个值就是0。这样可以保证该变量为强符号,当名字冲突时,可以发现,而不是被未知的值覆盖。三嘛,最好能够避免全局变量,或者定义一个独一无二的名字。
再多说一点,在编译阶段,还可以通过-fno-common选项来禁止将未初始化的全局变量放入到common段。
同样的文件,看下面的编译链接过程:
[root@Lnx99 test]#gcc -g -fno-common -c
test1.c
[root@Lnx99 test]#gcc -g -fno-common -c
test2.c
[root@Lnx99 test]#gcc -g -fno-common -o
test main.c test1.o test2.o
test2.o:(.data+0x0): multiple
definition of `init'
test1.o:/root/work/test/test1.c:6: first
defined here
collect2: ld returned 1 exit status
下面针对该书3.3.3节BSS段的内容进行更细节的探讨——该节内容不在本文中重复说明了,只说一下结论。对于全局变量来说,如果初始化了不为0的值,那么该全局变量则被保存在data段,如果初始化的值为0,那么将其保存在bss段,如果没有初始化,则将其保存在common段,等到链接时再将其放入到BSS段。关于第三点不同编译器行为会不同,有的编译器会把没有初始化的全局变量直接放到BSS段。
关于上面这个结论,我就不重复进行探讨了,下面探讨一下更细节点的问题。从上面的内容上看,尽管未初始化的全局变量有可能在编译阶段被保存在common段,但是最终还是会放到BSS段。那么我们是否可以将未初始化的全局变量与初始为0的全局变量等同起来呢?
请看下面的代码:
文件test1.c:
#include
int init = 0;
void init1()
{
if (0 == init) {
init = 1;
printf("init1\n");
}
}
编译gcc -g -c test1.c
文件test2.c:
#include
int init = 1;
void init2()
{
if (init) {
init = 0;
printf("init2\n");
}
}
编译gcc -g -c test2.c
文件main.c
void init1();
void init2();
int main()
{
init1();
init2();
return 0;
}
编译gcc -g -o test main.c test1.o test2.o
这时会报错
test2.o:(.data+0x0): multiple
definition of `init'
test1.o:/root/work/test/test1.c:4: first
defined here
collect2: ld returned 1 exit status
这个报错大家都可以接受吧?OK,那么现在我们做一个小小的改动,将文件test1.c中的int init = 0改为int init,对于test1.c来说,这个改动不影响其逻辑,因为init如果未初始化,其值也应该是0。
当前的test1.c
#include <stdio.h>
int init;
void init1()
{
if (0 == init) {
init = 1;
printf("init1\n");
}
}
编译gcc -g -c test1.c
gcc -g -Wall -o test main.c test1.o test2.o
这次即使使用-Wall打开所以warning,也没有任何警告和错误,已经生成了输出文件test。
执行test
[root@Lnx99 test]#./test
init2
好,下面探讨一下为什么是这样。第一种情况,当test1.c中的init被初始化为0时,尽管init被放置在bss段,但是它是一个强符号。而test2.c中,定义了init为1,也是一个强符号,所以引发了错误。第二种情况,当test1.c中的init不进行初始化,尽管其值仍然为0,但是其被保存在common段,为一个弱符号。当test2.c中定义了init为1一个强符号,那么在链接的过程中,gcc会用这个强符号覆盖掉弱符号,并不会引起链接冲突错误。但是在运行阶段,进入init1时,这个init的值却并不是其所期望的值,因此导致没有打印init1。
我想关于BSS与COMMON段的这点不同,应该讲得比较清楚了。从这个区别中,我们需要注意,当定义全局变量时,有两点需要注意:一,如果只有本文件使用,那么需要添加上static;二,即使不能使用static,那么一定要为该全局变量定义初值,即使这个值就是0。这样可以保证该变量为强符号,当名字冲突时,可以发现,而不是被未知的值覆盖。三嘛,最好能够避免全局变量,或者定义一个独一无二的名字。
再多说一点,在编译阶段,还可以通过-fno-common选项来禁止将未初始化的全局变量放入到common段。
同样的文件,看下面的编译链接过程:
[root@Lnx99 test]#gcc -g -fno-common -c
test1.c
[root@Lnx99 test]#gcc -g -fno-common -c
test2.c
[root@Lnx99 test]#gcc -g -fno-common -o
test main.c test1.o test2.o
test2.o:(.data+0x0): multiple
definition of `init'
test1.o:/root/work/test/test1.c:6: first
defined here
collect2: ld returned 1 exit status
相关文章推荐
- ubuntu下nginx的安装
- POJ 1321 棋盘问题 dfs
- RESTful API URI 设计: 查询(Query)和标识(Identify)
- regular expression matching DP
- GNU Binary Utilities的笔记
- 【codevs1039】01年noip TG--数的划分满分dp+愚蠢深搜+pas的奇怪做法
- C++编写简易的飞机大战
- KIDx's Pagination (周赛1)
- Python 3.4 学习总结(1)
- poj 1089 Intervals(区间合并问题)
- 树的遍历方式(不知道对不对)
- Linux常用命令大全
- hdu-2100-lovekey
- CodeForces 545A
- C++中引用(&)的用法和应用实例
- hongyang的博文深入理解Android中的自定义属性 学习笔记
- Vanya and Lanterns
- linux系统的颜色配置
- 快速幂
- Parcelable接口的使用