码迷,mamicode.com
首页 > 其他好文 > 详细

为什么全局变量一定要初始化?

时间:2019-09-02 23:37:18      阅读:167      评论:0      收藏:0      [点我收藏+]

标签:出错   赋值   全局变量   lin   多个   初始化   style   运行   its   

一、初始化规则部分

在说明为什么要初始化之前,先提及下 C 语言的初始化规则,以备后用。
可能大家在对数组进行初始化时用的是这样的方法:

char buf[10] = {0};

那么 char buf[10] = {1};是不是将每个数组中的每个元素都初始化为 1 了呢?

其实不然,根据编译器的特性,在指定初始化元素时,如果元素的个数少于数组元素的总个数,那么其它的元素将会初始化为 0。

我们可以用一段代码来验证这个特性:

1 #include <stdio.h>
2 
3 int main()
4 {
5   char buf[10] = {1};
6 
7   return 0;
8 }

其反汇编代码如下:

 1 <main>:
 2   push {fp} ; (str fp, [sp, #-4]!)
 3   add fp, sp, #0
 4   sub sp, sp, #20
 5   sub r3, fp, #16
 6   mov r2, #0
 7   str r2, [r3]
 8   str r2, [r3, #4]
 9   strh r2, [r3, #8]
10   mov r3, #1
11   strb r3, [fp, #-16]
12   mov r3, #0
13   mov r0, r3
14   add sp, fp, #0
15   pop {fp} ; (ldr fp, [sp], #4)
16   bx lr

由其中的部分编译代码 6-11 行可知,程序只是将数组的第一个字节赋值为 1,而其余字节被赋值为 0。

 

二、全局变量初始化部分

下面说一下我们创建的全局变量时为什么要初始化,如果想创建一个初始值为0的变量,那么 “ = 0 ” 的赋值操作可不可以省略呢?

费话不多说,用代码来说明一切。

假如有以下场景,某公司为实现一个项目,A程序员编写了 module_a.c 程序,他的同事B编写了 module_b.c 程序,最终两个人编写的源码如下:

 1 /* module_a.c */
 2 #include <stdio.h>
 3 
 4 void function(void);
 5 
 6 int global = 0;
 7 
 8 int main()
 9 {
10     global = 3;
11     function();
12     printf("main: %d \n", global);
13     return 0;
14 }
 1 /* module_b.c */
 2 #include <stdio.h>
 3 
 4 int global;
 5 
 6 void function(void)
 7 {
 8     global = 6;
 9     printf("function: %d \n", global);
10     return 0;
11 }

二人心有灵犀,定义了同样的全局变量 global,但由于 B 一时的疏忽,忘了将 global 进行初始化。

编译正常通过,且运行结果如下:

function: 6
main: 6

我们可以看到,同事 A 编译的模块产生了莫名其妙的运行结果。为什么定义了同名的全局变量,编译器不会报错呢? 我们继续探索。

首先将源文件分别编译为目标文件,然后使用 readelf 工具查看内容。

arm-linux-gcc -c module_x.c
arm-linux-readelf -a module_x.o

作者只摘取了其中的有关内容,其中 a 模块中的 global 标识如下:

    ...
[4] .bss    NOBITS    00000000 000078 000004 00 WA 0 0 4
    ...
13: 00000000    4 OBJECT GLOBAL DEFAULT    4    global
    ...

b 模块中的 global 标识如下:

    ...
12: 00000004    4 OBJECT GLOBAL DEFAULT COM global
    ...

由此可得知,初始化为 0 的全局变量是合并至 .bss 段,而未初始化的全局变量合并到了 COM(common block) 段。原因是 gcc 编译器的缺省行为和传统 unix c 编译器一致,将未初始化的全局变量放入到 common block 中, common block 相当于弱符号(weak symbol),所以链接的时候并不会报错,这或许可能是一个很难被找出的 BUG。

 

在网络上找到了以下总结:

  同名的弱符号和 global 符号链接不会出错,链接器会选择 global 符号。同理,如果有一个 global 符号和多个在 common block 中的重名,那么链接器会选取global符号。换句话说,链接器认为未初始化的全局变量是weak symbol。

当然,避免这种情况发生的办法也是有的,我们可以在编译时加上 -fno-common 属性来关闭 gcc 的这个特性,如果有同名的全局变量,链接时就会产生出错误,便于用户知晓问题所在。

为什么全局变量一定要初始化?

标签:出错   赋值   全局变量   lin   多个   初始化   style   运行   its   

原文地址:https://www.cnblogs.com/GyForever1004/p/11448828.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!