一般情况下,未明确赋初值的全局变量会被自动清零,而且ANSI C中似乎也有这一规定,之前写程序都默认这是没有例外的规定了。然而,前段时间在调试28030程序的时候发现,未赋初值的全局变量并没有被清零,而是一个随机值,这说明在TI的DSP中,全局变量并不会自动清零。TI论坛中的这个讨论也证实了这一结论,由此可见,TI的编译器的确有些很特别的地方……下面来具体谈谈全局变量清零这个问题。
全局变量一般被称为.bss
段,ARM编译器中也将其称为ZI-data
段,即”Zero Initialize”之意。.bss
段是不占用ROM空间的,程序运行起来后.bss
段显然应该是在RAM中的,这就有一个问题,RAM中的.bss
段是什么时候完成清零的呢?答案是在进入main()
函数之前,由启动代码或库函数完成清零操作,如果使用了操作系统,则由Bootloader完成这一工作。
以ARM MDK附带的启动代码为例,在执行完使用汇编语言编写的启动代码后,程序并没有跳转到main()
处,而是跳转到了__main()
处执行。__main()
函数是一个库函数,这个函数最重要的作用就是完成所谓的“段拷贝”工作,具体来说,就是完成了以下三个步骤:
- 将非零(只读和读写)运行区域从其载入地址复制到运行地址;
- 清零
ZI-data
区域(即.bss
段); - 跳转到
__rt_entry
,最终会跳转到main()
处,用户程序开始执行。
由此可见,是否对全局变量进行清零完全是由启动代码和库函数决定的,而TI的编译器就没有链接完成这一工作所需的库函数,自然也就无法完成全局变量自动清零的工作了。解决这一问题的方法有两个:
- 在声明全局变量显式的赋初值0
- 程序中仿照
__main()
库函数的写法,自己写一个清零函数完成这一工作
值得注意的是,通过请教导师得知,这并不是TI编译器的设计缺陷,TI的编译器是专门这样设计的,这样设计的目的在于赋予程序员更大的灵活性。在某些情况下,是需要对全局变量进行选择性清零或其它操作的。
一个实际的例子是:假设使用了看门狗,在某些情况下系统看门狗复位了,对于某些控制系统而言,这时是要求系统在最短的时间内恢复之前的工作状态的。这种情况下,一般在程序开头加上一段检测代码,检测复位原因,如果是正常上电复位,则对全局变量进行清零操作;若判断出是看门狗复位,则自动执行之前的程序。系统软复位时并没有掉电,故RAM中的数据不会丢失。因为此时并没有对全局变量进行清零,之前的全局变量数据全部都在,此时就可以很快的恢复之前的工作状态,并且还可以使用一些全局变量辅助判断之前的错误复位的原因。