我们课本上讲的,我有点不太明白……
因为严格意义上的常量是一种「等效承诺」,只要结果一致,一个常量表达式可以翻译成任何东西,它和内存没有映射关系,所以正常来说在内存里是没有对应地址的。
而表达式则是一种还没有完成的逻辑,取决于表达式的返回值,如果返回值是变数,那么其实是可以取地址的,如果是常量,则取地址是无法完成的。
常量的问题比较复杂,放在前面说
-----
直接看程序和指令吧
const int i = 10; int j = 20;
int main() { int x = i; int y = j; }
编译后的汇编(由G++编译的ATT汇编):
全局变数 j 被标记好地址(_j)放在内存里(红框)
然后 y = j 的表达式当中实际指令(红色箭头),内存块指针(RIP/EIP寄存器)中取 _j 标记的地址,赋值给ecx寄存器(也就是y变数当前代表的寄存器)
再看常量i……
赋值 x = i 简单粗暴的指令(蓝色箭头),立即数 $10 写入x在栈里的内存(rbp)-&> (-4)
不熟悉汇编的话可以不用在一寄存器的这些细节,关键问题就是:上面的指令来看,编译器压根就没给常量i分配内存,也就没有地址这个概念
----
常量对编译器而言,是一种特殊的注释,他告诉编译器:只要是等效(等值)的方式,这个表达式可以用任何形式表达,不拘泥于内存、寄存器或是指令,可以和任何其他编译逻辑整合在一起,不用关心相互作用问题,编译器可以用更多的手段去优化代码。
对于语言本身而言,常量代表著这个表达式是孤立的,无状态且具有原子性的;这样的情况下,常量引用即复制,不需要生命周期管理,不需要考虑出上下文关系
所以……取地址这个操作打破了上面所需要的承诺
对于编译器,常量如果能够取地址,就必须不断追踪这个常量的内存地址,必须在不同的物件之间做链接,这样的常量各项操作和优化都和变数没区别。
对于语言本身,常量如果能够取地址,就必然存在生命周期维护,必须对逃逸进行检测,必须在代码块之间处理共享问题,必须对常量的引用追根溯源,这样的常量设计上没有意义,起不到他本该有的功能
表达式的话就要看情况了:
int i = 10;
//(错误)常量表达式不能取地址 auto a = (i+1); //(错误)常量更不能取地址了 auto b = (10);
auto j=i; auto k=j; //(可行)返回值是变数的话,表达式地址取地址是OK的 auto c = (*j); auto d = (*k);
严格地说,是取地址运算符不能作用于常量字面量上。因为从翻译后的二进位的角度来讲,这些直接书写在代码里的常量字面量是由硬体直接产生的,是不具有地址属性的。例如下面的对 1 对 a 取地址就是非法的:
const int * pi = 1; const char * pc = a;
但是 C/C++ 里平常说的常量一般指的是常变数,即经 const 关键字修饰的变数。const 变数经初始化赋值后不可再修改。但是从二进位的角度来讲,它与变数无任何区别,同样是存储在内存中,因此可以取地址。const 的属性只是编译器层面在编译时候的检查与保护而已。例如,下面的代码就是合法的:
int i; scanf("%d", i); const int ci = i; const int * pi = ci;
char c; scanf("%c", c); const int cc = c; const int * pc = cc;
至于表达式不能取地址我个人觉得这话也是说不清道不明的。表达式确实是不存在取地址的概念。但不代表表达式的运算结果不能取地址。
int a[5] = {0, 1, 2, 3, 4}; int * p = (*(a + 2));
建议你还是把你书拍个图放上来吧。你自己说难免会使书本上原来的意思变形。
内存里面你的程序占用的空间被划分为代码区和数据区,数据区又划分为不变数区,变数区,堆区,栈区等等。有一些常量在代码区,如果这个所谓的常量写在代码里面的话,比如
var a = b + 常量
那么编译系统认为在正常情况下你不可能有获取a的地址的需求,除非你是做某些非法hack操作。我认为表达式也是这一类。还有一些常量在数据区里面的不变数区,比如
const int a = 123
系统也不认为你有获取这些常量地址的需求,因为这些数据是禁止修改的。
上网查下什么叫左值和右值,你就明白了
上面那个大兄弟说的很清楚,我可以给你一个直白简单的
就是说,内存里放的位置不一样,内存分很多区域,常量区代码区栈堆等等等等,因为常量存放在内存里的位置没有"地址"这个概念,所以不能取地址。
说明写书的人不懂什么叫常量什么叫常数,常数12在text段,是指令地址,拿到地址也是没有意义的,所以12非法。而常量const是可以取地址的。
常量和表达式只存在于代码中,不存在于堆、栈中。因此没有地址