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


    一、初始化规则部分

    在说明为什么要初始化之前,先提及下 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。到此可以得出结论,并非是所有的元素都被初始化为 1。

    二、全局变量初始化部分

    下面说一下我们创建的全局变量时为什么要初始化,如果想创建一个初始值为 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 
    ", 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 
    ", 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 的这个特性,如果有同名的全局变量,链接时就会产生出错误,便于用户知晓问题的所在。

  • 相关阅读:
    Vue(五)模板
    2.typescript-你好世界
    1.typescript-安装
    jquery事件冒泡
    jquery中animate的使用
    Python 环境管理
    CentOS yum 源修改
    Node.js
    端口
    CSV
  • 原文地址:https://www.cnblogs.com/GyForever1004/p/11448828.html
Copyright © 2020-2023  润新知