我们课本上讲的,我有点不太明白……


因为严格意义上的常量是一种「等效承诺」,只要结果一致,一个常量表达式可以翻译成任何东西,它和内存没有映射关系,所以正常来说在内存里是没有对应地址的。

而表达式则是一种还没有完成的逻辑,取决于表达式的返回值,如果返回值是变数,那么其实是可以取地址的,如果是常量,则取地址是无法完成的。

常量的问题比较复杂,放在前面说

-----

直接看程序和指令吧

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是可以取地址的。


常量和表达式只存在于代码中,不存在于堆、栈中。因此没有地址


推荐阅读:
相关文章