先从一段代码说起:
int main()
{
int *p;
int const a = 10;
p = (int *)&a;
*p = 20;
printf("a = %d\n", a);
printf("*p = %d\n", *p);
printf("the address of a: %p\n", &a);
printf("the address of p: %p\n", p);
getchar();
return 0;
}
如果放在C中运行(.c文件),结果为:
a = 20
*p = 20
the address of a: 000000A03DDAF7E4
the address of p: 000000A03DDAF7E4
如果放在C++中运行(.cpp文件),结果为:
a = 10
*p = 20
the address of a: 0000004E93EFFAD4
the address of p: 0000004E93EFFAD4
如果你细心观察,肯定会问:为什么在C++中,指针p与a表示同一块内存,竟然内存中的值不一样?
原因如下:
一 c语言中的常量不能直接修改,但是能通过指针访问间接修改。
C语言中会为const常量单独分配内存,只要修改了内存中的空间,就会影响常量本身。
二 C++中,const表示一个真正的常量。
C++中在const修饰的常量编译期间,值就已经确定下来了。
const int len = 10; intarr[len]; //在C++中能编译通过,C语言中编译出错
C++编译器对const做了加强,当C++编译器扫描到const常量声明时,它不会像C语言一样为直接为const单独分配内存。 定义int const a = 10;之后,C++编译器会将a放到一个符号表里面,符号表的存在形式为key-value的形式。我们定义常量a=10,key就是a,value就是10。这个值就是锁死了,不能变的。 const变量在预处理时处理,编译器只对其值读取一次。 用到a的时候,编译器根本不会去进行内存空间的读取,会从符号表里面把a的值拿出来。
这就是C++的 常量折叠(constant folding) ,即将const常量放在符号表中,而并不给其分配内存。编译器直接进行替换优化。除非需要用到a的存储空间的时候,编译器迫不得已才会分配一个空间给a,但之后a的值仍旧从符号表中读取,不管a的存储空间中的值如何变化,都不会对常量a产生影响。但是在C语言中却不是这样,C没有constantfolding的概念。 用const定义一个常量的时候,编译器会直接开辟一个内存空间存放该常量,不会进行优化。
当然,你如果在C++中不想要这种编译优化,想让读取a的值的时候,不从符号表中去读取,而是从内存中去取。 可以用这样的定义方法:
const volatile int a = 10;
此时,上面代码的输出结果就会成为:
a = 20
*p = 20
the address of a: 0000004E93EFFAD4
the address of p: 0000004E93EFFAD4
当使用&操作符取局部const常量的地址时,会分配临时的内存空间,存储在堆栈中,可通过指针修改临时空间的值;所以,编译器会为a单独的开辟一块内存空间,p = (int )&a;然后这个内存空间赋给指针p,就是p指向这个内存空间。通过p去间接修改的值,是这个新开辟的内存空间的值,而不是符号表中a的值,我们让*p=20、30、100…修改的都是新开辟内存空间中的值。不会影响到常量本身的值。
但是当const常量为全局常量时,会在全局数据区分配内存,这样的语义会产生未定义行为:
#include <iostream>
const int g = 10;
int main()
{
int* p = (int *)&g;
*p = 20; //段错误
std::cout << "test\n";
getchar();
return 0;
}
const 和 #define 的对比
C++中的const常量与宏定义不同:
const常量是由编译器处理的,提供类型检查和作用域检查;
宏定义由预处理器处理,单纯的文本替换。
参考:
https://blog.csdn.net/zz460833359/article/details/48917217
https://www.cnblogs.com/WindSun/p/10207934.html
https://mp.weixin.qq.com/s/CE39RrsPhns9Ri-E9ODv2g