在学习C语言时,无数本教材都告诉我们:用const 类型 标志符; 这种方式是定义一个常量,也就意味着不可以修改。但真的只是这样吗?例如:
int main () {
const int var = 0x1;
var = 2;
printf("%d\n", var);
return 0;
}
你可能会说: var是不可修改的。从表面上看,似乎是这样,现在来编译一下,编译器提示以下的错误。
app.c(4): error: #137: expression must be a modifiable lvalue
编译器提示var必须是可以修改(modifiable)的量。var真的不可以修改吗?
现在,我们对代码进行一下修改。通过这个修改你可以发现const声明的常量是可以修改的。请看代码:
int main () {
const int var = 0x1;
int * pVar = (int *)&var;
*pVar = 0x2;
return 0;
}
这段代码,如果我们把它放在Keil + STM32平台上跑,并且将var和pVar加入变量watch窗口中,会发现下面的效果:
我们惊奇地发现:用const声明的var值,可以通过指针pVar间接修改!。为什么会这样?
分析上面的代码可以发现,原因非常简单:
在Keil + STM32平台上,用const声明的var实际上是在堆栈空间中分配的,也就是说,从物理上来看,var是分配在内存中。而此时硬件上也没有开启任何内存检查的机制,所以,我们可以间接地通过PVar指针对这个变量进行修改。
既然如此,那用const声明还有什么用?
可以从以下几个方面来理解const常量的优势。
通过将const声明的常量放置到函数外部作为全局,链接器会自动将该常量分配到Flash中。这样可以节省内存开销。
观察上图可以发现,var被分配在了地址0x080000388,而此空间正好在Flash区域。
现在的编译器够智能,看到了const就知道我们不想对该变量进行任何修改,自动分配到不可随意写的Flash中。既有效地保护了该常量,又节省内存空间,一举两得。
对我们想要只读的常量加上const,可以让编译器在编译过程中,发现代码中任何试图”违反规则,企图修改”该变量的任何代码。
例如,本文最开始的编译错误。甚至于在写代码时,好的编辑器都能实时帮我们检查这种错误。
此外,由于加const,其他的程序员在阅读你的代码时,能够直接了当知道这个常量是不应修改的。特别是在传递一些函数的参数时,例如:
void process (const char * var) {
// 一些代码
}
我们不需要再加注释说明var是不是会在process中修改。其他人只需要看到这个函数声明,就知道传入的指针不会被间接使用修改所指向的变量。
总结以上内容,我们对const有了一个新的了解:const声明的常量并不一定是不可修改,其主要功能是注明其不应当被修改。之后编辑器、编译器、链接器可以自行根据该声明,做出相应的处理,从而实现以下的几个特性。
注:以上测试均在Keil + STM32平台上实现。在其它平台上,const常量可能确实是不可修改的。本文的结果也指出, const常量并不一定不可修改。
本文首发于微信公众号: 01课堂 i01ketang.cc http://01ketang.cc