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 条评论
登录 后参与评论

相关文章

来自专栏从零开始学 Web 前端

从零开始学 Web 之 JS 高级(三)apply与call,bind,闭包和沙箱

不同的是传入参数时,apply 有两个参数,第二个参数是数组;call 从第二个参数开始是调用其的函数的所有参数。

623
来自专栏Crossin的编程教室

【Python 第68课】函数的参数传递(2)

接着上一次的内容,来介绍一种更加灵活的参数传递方式: def func(*args) 这种方式的厉害之处在于,它可以接受任意数量的参数。来看具体例子: def...

2546
来自专栏sunseekers

啊,函数呐!!!

一份需要你补充完整的函数导图!我还是一个初学者,这篇文章是我所知道的所有关于函数的知识,如有不完善或者错误,希望能够在评论下方指出,哈哈哈,大神勿喷。

562
来自专栏程序员互动联盟

【编程基础】如何赢得C++面试

1.new、delete、malloc、free关系 delete会调用对象的析构函数,和new对应的是free,free只会释放内存,new调用构造函数。m...

3537
来自专栏Kevin-ZhangCG

[ Java面试题 ]基础篇之二

1435
来自专栏aCloudDeveloper

C++中引用详解

引用简介   引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。   引用的声明方法:类型标识符 &引用名=目标变量名;   【...

1665
来自专栏前端学习心得

JavaScript基础(一)----强制转换与自动转换

JavaScript是一门动态语言,所谓的动态语言可以暂时理解为在语言中的一切内容都是不确定的。比如一个变量,这一时刻是个整型,下一时刻可能会变成字符串了。虽然...

812
来自专栏阮一峰的网络日志

图解 Monad

函数式编程有一个重要概念,叫做Monad。 ? 网上有很多解释(这里和这里),但都很抽象,不容易看懂。我尝试了好多次,还是不明白Monad到底是什么。 ? 昨天...

3204
来自专栏Python研发

最全Python内置函数

判断真假,  True:真  ,  False:假,   把一个对象转换成bool值

982
来自专栏柠檬先生

Java 基础标识符

标识符: 程序员为自己定义的类,方法或者变量等起的名称。     标识符由大写字母,数字,下划线(_)和美元符号组成,但不能以数字开头。 Java 语言中严格区...

1815

扫码关注云+社区