笔者在跟同事讨论变量存放的分区问题时,讨论到了const标识符所标记的变量能不能修改这个问题。
按直观理解,const关键字标识的变量,意味不能修改的变量,引申理解,猜测其可能存放在.rodata分区(只读)。
========================================================================
来实际操作验证一下,一看便知。
环境:选用Ubuntu平台,x86 gcc工具。
一、局部的const变量
代码验证:首先是局部的const变量
#include <stdio.h>
#include <string.h>
//const int global_const_vol = 84;
char *s = "abcde";
int main()
{
const int const_vol = 11;
return 0;
}
C语言的字符串变量会放在rodata段,我们以此作为参考
gcc -c选项可以只将.c文件编译成目标文件(.o),而不进行链接,使我们分析自己写的代码少了很多冗余代码,方便分析。
objdump -xsd 可以将目标文件中各个段的内容都打印出来。
我们只需要找我们定义的两个变量就好了,const_vol,s。
可以看到.data段中啥也没有,我们的s变量被放在了rodata段,存放的就是我们初始化的abcde变量,而const变量不知道去哪里了。
答案是在.text段(代码段)里,对比以下代码和dump结果便知。
include <stdio.h>
#include <string.h>
//const int global_const_vol = 84;
char *s = "abcde";
int main()
{
// const int const_vol = 11;
return 0;
}
编译 && dump
很明显,.text段中的c745fc0b这块数据丢了。
并且.data段的大小没变化
我们可以得出结论:本环境下的局部const变量存放于text段中,程序运行时会将此变量放置于栈区。
既然在栈中,那其本身就应该处于可以修改的内存位置。
我们先直接对其进行修改。
int main()
{
const int const_vol = 11;
const_vol = 100;
printf("const_vol = %d\n",const_vol);
return 0;
}
编译
报错,那显然不能执行了。
直接对其地址进行修改呢?
int main()
{
const int const_vol = 11;
int *pointer = &const_vol;
*pointer = 100;
printf("const_vol = %d\n",const_vol);
return 0;
}
编译器只进行了警告,我们的const变量也成功被改写了;
结论:对局部的const变量地址进行操作,完全可以重写const变量中的内容;
二、全局的const变量
首先第一个问题,全局const变量存放的位置。
代码:
const int global_const_vol = 10;
char *s = "abcde";
int main()
{
return 0;
}
编译成.o文件,并ojbdump:
可以看到rodata中除了abcde以外,多了0a000000。
这是由于是小端字节序的文件,正确的顺序应该是0000000a,即10,我们的全局const变量。
得出结论:全局const变量是存放于rodata区的。
那能否进行修改呢?
const int global_const_vol = 10;
char *s = "abcde";
int main()
{
int *pointer = &global_const_vol;
*pointer = 100;
printf("global_const_vol = %d\n",global_const_vol);
return 0;
}
编译只有warning,和局部的const变量一样
但执行就报段错误了,因为我们修改的是rodata段的变量。
结论:全局const变量是存放于rodata区的,无论如何都不能修改。
总结
在gcc工具链中,局部const变量不能直接修改是由编译器控制的,其本身存放于代码运行时,临时分配的函数栈中。
而全局的const变量存放于rodata分区,任何时候都是只读变量。