前言
(1)今天看到一个有意思的问题,在交流群中,一位网友问,全局变量为什么不能给变量。会出现initializer element is not constant报错,代码如下
#include <stdio.h> int a = 1; int b = a+1; //这里会报错initializer element is not constant int main(void) { printf("a = %d\r\n",a); printf("b = %d\r\n",b); return 0; }
(2)一般来说,全局变量我们都是默认给的一个常量数字,局部变量赋值给一个变量。关于上述问题,我也表示疑惑,于是我带着疑问,开始查找资料。
#include <stdio.h> int a = 1; int main(void) { int b = a+1; printf("a = %d\r\n",a); printf("b = %d\r\n",b); return 0; }
解释
全局变量和局部变量存储位置
(1)想要弄清楚这个问题,首先,我们需要知道全局变量和局部变量存储有什么区别?想了解具体信息,请看:RAM明明断电会丢失数据,为什么初始化的全局变量存储在RAM?详细分析程序的存储;
(2)因为上面的文章篇幅比较大,所以我浓缩一下。
<1>被赋值为非0的全局变量,最终会存储在.data段。他在编译时候,会变成RW-data,他的初始值是需要存储在ROM区域的。
<2>而局部变量,并不会被编译进入单片机,也就是说,他在单片机断电状态是不占空间的。
<3>当单片机上电之后,会给全局变量在RAM中分配出一个静态数据区。同时把初值赋值到这个静态数据区的.data段。
<4>单片机上电并且开始运行程序之后,调用到了局部变量所存在的那个函数时候。单片机才会给局部变量在栈中分配空间,然后赋初值是在运行过程中,执行代码实现的。
<5>因此,我们可以得出结论。被赋初值的全局变量,初始值是静态存储。而被赋初值的局部变量,初始值是动态存储。
(4)了解了上述之后,我们就能够明白为什么局部变量赋初始值可以被通过了,因为他是动态的,所以编译器不会管他。但是为什么全局变量又不可以呢?这就涉及,静态存储的数据与编译器之间的关系了。
编译器有关
(1)我们都知道,编译器能够将C语言变成机器所能够识别的01语言。而编译器的还有一些功能,常常被我们所忽视了。
(2)为了能够让程序更加小巧精炼,编译器会帮我们处理很多事情。这就引出了一个概念,用编译器的时间换运行态的时间。
<1>编译时:您(开发人员)编译代码的时间段。
<2>运行时间:用户运行您的软件的时间段。
(3)例如下面这个简单的代码:
<1>我们能够发现int b = 2 + 3;转换称为汇编代码之后,他是直接将5传递给了b。这个就是编译器进行了运算处理,对代码进行了优化。这个叫做常量折叠。
<2>sizeof这个运算同理,编译器会直接将int是4个字节这个信息传递给变量。
(4)上述两个例子都是用编译器的时间换运行态的时间。那么讲了这么多,和全局变量初始化元素必须是常量有什么关系呢?
(5)上面说了,编译器会进行常量折叠,但是请注意,这里是常量折叠!常量折叠会将常量表达式在编译器运行时期计算出来,整型字面量、sizeof()运算符(当参数不是VLA时),枚举常量,以及各种算术、关系/比较运算符在所有操作数都是整型常量时,整个表达式都会算是整型常量表达式。
(6)但是,我们发现了没有,编译器是不会进行变量的运算的!因此,全局变量int b = a+1;这样赋初值,编译器是不会进行运算的!
总结
(1)因此,我们可以得出结论:
<1>编译器无法在编译时求得一个非常量的值,它只能在运行时通过读取变量地址来间接得到变量的值,而全局变量在编译时就必须确定其值。
<2>局部变量的初始值是不由编译器管理,他是在运行时进行的分配,因此可以初值可以赋予变量。