const常量机制分析

const常量机制分析

const常量机制分析

const为C/C++常用的修饰符,表示该变量是一个常量,不可被修改等含义。那么在实际使用中会存在如下疑问:

1,const修饰的变量是否真的不可修改?

2,如果被修改,会出现什么问题?

3,C和C++中实现机制一样吗?

4,对于内置类型和自定义类型数据,const实现原理一样吗?

5,为什么const变量可以被定义在.h头文件中?

问题1, const修饰的变量是否真的不可修改?

1.1 编译器保证了const修饰的变量不能被修改或者重新赋值。

const int var = 10;

var = 20;//编译报错,不能被修改

1.2 通过指针修改const变量。

const int var = 10;

int* ptr_const = (int*) (&var);

ptr_const = 20;

1) 局部const变量,对于C++程序,该变量地址中的值可以被修改,但是对其的修改不会影响变量实际被引用地方的值。对于c程序,该变量可被修改,且变量使用地方也会受到影响。

对于C++程序:

int main(int argc,char**argv)

{

const int var = 10;

int* ptr_const = (int*) (&var);

printf("ptr_const= %x, var= %x\n", ptr_const, &var);

printf("var= %d, *ptr_const=%d\n", var, *ptr_const);

*ptr_const = 20;

printf("var= %d, *ptr_const=%d\n", var, *ptr_const);

printf("ptr_const= %x, var= %x\n", ptr_const, &var);

可见,对变量地址的值进行修改后,var使用的值也变化,为最新的值。

2)对于全局变量,静态局部变量,全局静态变量都不能被修改,否则会core。

代码:

const int var = 10;

int main(int argc,char**argv)

{

int* ptr_const = (int*) (&var);

printf("ptr_const= %x, var= %x\n", ptr_const, &var);

printf("var= %d, *ptr_const=%d\n", var, *ptr_const);

*ptr_const = 20;

printf("var= %d, *ptr_const=%d\n", var, *ptr_const);

printf("ptr_const= %x, var= %x\n", ptr_const, &var);

return 0 ;

}

运行结果:

可以看到var变量的地址在0x4008c8。

我们来看看0x4008c8是位于程序的的哪个段。

执行命令

readelf -s msgq

看到符号var地址和程序输出的完全一致。其对应的Ndx下标为14,表明该变量存储在msgq文件中的下标为14的段。

执行命令

readelf -S msgq

可见,对变量地址的值进行修改后,var使用的值也变化,为最新的值。

2)对于全局变量,静态局部变量,全局静态变量都不能被修改,否则会core。

代码:

const int var = 10;

int main(int argc,char**argv)

{

int* ptr_const = (int*) (&var);

printf("ptr_const= %x, var= %x\n", ptr_const, &var);

printf("var= %d, *ptr_const=%d\n", var, *ptr_const);

*ptr_const = 20;

printf("var= %d, *ptr_const=%d\n", var, *ptr_const);

printf("ptr_const= %x, var= %x\n", ptr_const, &var);

return 0 ;

}

运行结果:

可以看到var变量的地址在0x4008c8。

我们来看看0x4008c8是位于程序的的哪个段。

执行命令

readelf -s msgq

看到符号var地址和程序输出的完全一致。其对应的Ndx下标为14,表明该变量存储在msgq文件中的下标为14的段。

执行命令

readelf -S msgq

可以看到位于只读数据段.rodata,当然程序不允许修改该地址内的数据,所以会core。

结论:

对于全局变量,局部静态变量,全局静态变量,存储在程序的只读数据段,不能被修改。

2,如果const变量被修改,会出现什么问题?

在问题1中已经得到了结论和验证。

3,C和C++中实现机制一样吗?

3.1不同点:

对于局部const变量,C++在变量具体使用地方通过常量替换实现。C语言中表示只读的变量。

3.2 相同点:

都不能对只读数据段的常量进行修改。如:全局变量,静态局部数据变量,全局静态变量。

4,对于内置类型和自定义类型数据,const实现原理一样吗?

4.1局部变量

内置类型和自定义类型完全一样,都没有分配存储空间。运行时在栈空间存储,每次运行地址不定,可以通过指针修改其值。

代码

struct test

{

int var;

test(int x)

{

var = 30*40;

}

};

int main(int argc,char**argv)

{

const test testObj(5);

int* ptr_Obj = (int*) (&testObj.var);

printf("ptr_const= %x, var= %x\n", ptr_Obj, &testObj.var);

printf("ptr_const= %d, var= %d\n", *ptr_Obj, testObj.var);

*ptr_Obj = 40;

printf("ptr_const= %d, var= %d\n", *ptr_Obj, testObj.var);

}

运行结果:

4.2全局变量或者静态变量(全局和局部)

自定义类型const变量存分配空间,存储在bss段,且可以被修改。

内置类型const变量分配空间,存储在只读数据段.rodata,不能被修改。

代码

struct test

{

int var;

test(int x)

{

var = 30*40;

}

};

static const test testObj(3);

int main(int argc,char**argv)

{

int* ptr_Obj = (int*) (&testObj.var);

printf("ptr_const= %x, var= %x\n", ptr_Obj, &testObj.var);

printf("ptr_const= %d, var= %d\n", *ptr_Obj, testObj.var);

*ptr_Obj = 40;

printf("ptr_const= %d, var= %d\n", *ptr_Obj, testObj.var);

}

结果:

查看符号表

readelf -s msgq

查看段表

可以看到位于.bss段。

5,为什么const变量可以被定义在.h头文件中?

我们都知道,.h头文件中不能有定义。其中const变量,类和模版是特例。

5.1 在.h中声明const变量

在头文件中extern const int var;//声明

在cpp文件中只能定义一次,否则会出现重定义。

因为这种情况生成的.o文件符号表存在var。

5.2在.h中定义const变量

在头文件中定义const变量

const int var =10;

在多个cpp文件中引用该文件。并把cpp对应生成的.o文件链接为可执行程序。

可以正常编译链接,不会报错。

会对该const变量分配空间,且被重复存储在不同只读数据段。重复次数和引用该.h文件的cpp生成的.o文件个数一致。且各存储地址不一样,都不能通过指针被修改。

查看符号表。

可以看到存在两个var符号,地址不同。

运行结果

各cpp文件读取对应的只读数据段的数据,互不影响。

特别注意点:

对于头文件定义字符串指针常量的正确定义如下:

const char * const constCharPtr="hello const";

表示指针常量,则可以放在头文件。

而不是常量指针,下面这句在头文件中,如果被多个cpp包含。则会出现重定义。

const char *constCharPtr="hello const";

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏javathings

Java 内存模型简述

Java 内存在逻辑功能上分成 5 个区。方法区,堆区,JVM 栈,方法栈,程序计数器(PC 寄存器)。

762
来自专栏Python小屋

学习Python的利器:内置函数dir()和help()

(1)内置函数dir()用来查看对象的成员。在Python中所有的一切都是对象,除了整数、实数、复数、字符串、列表、元组、字典、集合等等,还有range对象、e...

3198
来自专栏Python小屋

Python 3.x中内置函数range()函数的用法

range()是Python开发中非常常用的一个内置函数,语法格式为range([start,] end [, step] ),有range(stop)、ran...

2944
来自专栏JAVA高级架构

为什么要用单例模式?

742
来自专栏desperate633

深入理解Java Runtime Area Java运行时数据区Java Runtime Area的分类从线程的角度理解Java Runtime Area从存储内容理解Java Runtime Are

具体的每个区域的内容和特点可以参考《深入理解Java虚拟机》,此书已经讲的很详细了。 下面我们对这几个数据区域进行分类,分别从不同的视角来分析,加深我们的理解

661
来自专栏C语言及其他语言

【优秀题解】绝对值排序】(合并排序详解+图解)

原题链接:http://www.dotcpp.com/oj/problem1169.html (大家可以自行提交) 解题思路: 1.采用分治法思想,把整个序列...

4788
来自专栏开发与安全

从零开始学C++之IO流类库(三):文件的读写、二进制文件的读写、文件随机读写

一、文件的读写 如前面所提,流的读写主要有<<, >>, get, put, read, write 等操作,ofstream 继承自ostream, ifst...

3750
来自专栏Petrichor的专栏

python: argparse库 & 命令行解析工具

1153
来自专栏专注 Java 基础分享

字节码文件的内部结构之谜

如果计算机的 CPU 只有「x86」这一种,或者操作系统只有 Windows 这一类,那么或许 Java 就不会诞生。Java 诞生之初就曾宣扬过它的初衷,「一...

3879
来自专栏软件测试经验与教训

上期答案

3295

扫码关注云+社区