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

Posted gyforever1004

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么全局变量一定要初始化?相关的知识,希望对你有一定的参考价值。

一、初始化规则部分

在说明为什么要初始化之前,先提及下 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 的这个特性,如果有同名的全局变量,链接时就会产生出错误,便于用户知晓问题所在。

以上是关于为什么全局变量一定要初始化?的主要内容,如果未能解决你的问题,请参考以下文章

请问C中结构体变量为全局变量或静态变量时才能初始化么?

Tensorflow全局变量初始化问题

Rust 全局变量

java中的全局变量和静态变量是在编译时分配内存还是在加载时分配内存??

Android高性能编程基本规范

C/C++ --- 全局变量初始化总结