为什么 const 引用可以指向常量还可以取到地址?

2018-09-24 00:09:59 +08:00
 ngg0707
#include <iostream>
using namespace std;

int main(int argc, char const* argv[])
{
    const int& a = 1;
    cout << &a << endl;
    return 0;
}

不知道该怎么搜索这个问题,没查到。有人知道吗?

5578 次点击
所在节点    C
44 条回复
jmc891205
2018-09-24 00:23:41 +08:00
编译器生成了一个临时对象
ngg0707
2018-09-24 00:27:39 +08:00
```cpp
#include <iostream>
using namespace std;

int main(int argc, char const* argv[])
{
int&& a = 1;
cout << &a << endl;
return 0;
}
```

这样居然也可以。
ngg0707
2018-09-24 00:28:23 +08:00
@jmc891205 您好,如果不这样做,是否编译器就不会给这个右值分配内存呢?
jmc891205
2018-09-24 00:36:45 +08:00
@ngg0707 对你给的这个例子来说是的 你不声明 const int&a 和 int &&a 的话 那是不会给常量 1 分配内存
anonymous256
2018-09-24 00:38:03 +08:00
对于 1,2.55 ,'a',这类的数据,它们是存放在寄存器上的,用于初始化等操作。没有所谓的内存地址,属于字面常量(literal constant)。如 int x = 5,x 是左值,5 是右值。x 是变量(左值)有地址,5 是字面常量(右值)无地址。

C/C++能凡是能取到地址的,该类被认为是左值。字面值常量被认为是右值,不允许取地址。

至于楼主提到的 const 常量,const 只是修饰符,只能说是 const 修饰后,该变量不能被修改,它和字面值常量(右值)有着本质的不同。

我了解的是这样了,欢迎指正。
ngg0707
2018-09-24 00:40:20 +08:00
@anonymous256 我觉得楼上解决了我的问题。你可以看看楼上的回答。
snnn
2018-09-24 00:47:34 +08:00
@anonymous256 完全错误!!!
jmc891205
2018-09-24 01:04:01 +08:00
@jmc891205 我在 4 楼有地方说的可能有歧义
不是「不会给常量 1 分配内存」
而是不会分配内存去存储常量 1
429839446
2018-09-24 01:04:54 +08:00
如果我没记错。
常量引用可以绑定右值。
常量引用本身是左值。
Sparetire
2018-09-24 01:41:48 +08:00
常量和变量不可变还是有区别的吧,我的理解是 const 还是运行时创建,而常量是编译时就确定的
raysonx
2018-09-24 02:00:02 +08:00
@anonymous256 然而你的理解是错误的。
另外 @ngg0707
其他架构的 cpu 我不敢说,在 x86 架构下,整数的右值通常直接对应汇编的立即数寻址,也就是直接保存在编译后的机器指令中,并不会被保存在寄存器。以你举的 int x=5 为例,编译后的汇编指令通常是这样的:

mov [x 变量的内存地址], 5

在实际的机器代码中就是保存在了二进制指令的几个字节中。在 cpu 执行到这一句的时候,动态载入到指定的内存地址(或寄存器)。

对于字符串,极有可能是保存在一个单独的段中。
xupefei
2018-09-24 02:28:44 +08:00
@raysonx #11 字符串赋值是是 mov dword ptr,本身存在可执行文件的 rdata 段里,通过 HEX 编辑器就能找到。
shoujiaxin
2018-09-24 03:08:17 +08:00
C++ Primer (第五版)中文版的第 55 页有解释
这是一种例外情况,初始化常量引用时允许使用任意表达式作为初始值!包括非常量的对象、字面值或者一般的表达式!只要该表达式的结果能转换成引用的类型即可。
字面值的常量引用实际是绑定到一个临时量
geelaw
2018-09-24 03:51:37 +08:00
那你觉得 int x; int const &y = x; 的话 &y 有没有意义呢?
snnn
2018-09-24 05:20:23 +08:00
这种垃圾代码你们纠结一半天有什么意义?现实工作中谁把代码写成这样先去好好反省下正确的该怎么写。
mintist
2018-09-24 09:51:45 +08:00
楼主,来看看汇编代码就晓得为啥还是能取到地址了,把代码再精简下,然后结合汇编来看下。


精简的 C++代码:

```c
int main(void)
{

const int &a = 1; // 将变量 a 指向常量的地址,后面使用时可直接引用使用


return 0;
}
```

对应在 ARM-gcc 下的汇编代码:注释是后面添加的

```asm
main:
sub sp, sp, #8 ; 在函数内申请栈空间,通过偏移 sp 来实现

mov r3, #1 ; 将立即数放到寄存器 r3 中
str r3, [sp] ; 将 r3 的值推到栈中
mov r3, sp ; 将 sp 值也就是栈地址保存到 r3 中,也就是放置立即数 1 的内存地址
str r3, [sp, #4] ; 将放置立即数 1 的内存地址放在栈中(带偏置),也就是我们所需要的 a 变量的值,后面需要引用 a 所指向的值时,编译器就去这个地址取出来,再指过去就好了。

; ARM 体系结果默认把第 1 个返回值放到 r0 中,所以在 bx 之前把 r0 的值 0 准备好就可以了
mov r3, #0
mov r0, r3
add sp, sp, #8
bx lr
```

所以,回到“为什么 const 引用可以指向常量还可以取到地址?”这个问题,在汇编代码看来,就是先把立即数 1 放到存储空间(这里是栈空间,如果是全局变量,那么就会在链接时到.rodata 段内存空间),然后再把 a 变量的本身也存下(存的值就是 1 的地址),用的时候取出来指过去就可以了。



详见如下的链接: https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(j:1,lang:c%2B%2B,source:'int+main(void)%0A%7B%0A++++const+int+%26a+%3D+1%3B%0A++++return+0%3B%0A%7D%0A'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:50,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:arm710,filters:(b:'1',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'1',intel:'0',trim:'0'),lang:c%2B%2B,libs:!(),options:'-fomit-frame-pointer',source:1),l:'5',n:'0',o:'ARM+gcc+7.2.1+(none)+(Editor+%231,+Compiler+%231)+C%2B%2B',t:'0')),k:50,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4
mintist
2018-09-24 09:54:43 +08:00
@snnn 可能楼主和我一样,是给自己设计的芯片写代码,然后给别人用的,所以需要比较纠结这些细节,,,
iwtbauh
2018-09-24 09:57:47 +08:00
@raysonx #11

可能他说的是 RISC 架构的计算机吧,你说的 x86 这种 CISC 计算机自然不一样。

int x = 5,在 RISC 上是这样工作的

mov 5, 寄存器 1
store 寄存器 1, (寄存器 2)

确实需要“放在寄存器”上

@anonymous256 #5

但是说法非常有歧义且容易混淆,立即常量是先在机器指令里,再在寄存器里。
iwtbauh
2018-09-24 10:05:23 +08:00
再回到 lz 的问题

为什么会有地址,其实编译器也很无奈啊,本来设计的当的编译器不会给 const 分配地址,让他做立即常量来避免多一次的内存访问(要知道访问内存可是开销很大的操作呀),但是 lz 偏偏要 &const,这相当于创建 const 变量的一个引用,于是编译器就不能把它优化掉了,所以 lz 才看到地址输出
wizardforcel
2018-09-24 10:29:14 +08:00
const 那种东西,你把它当成只读变量就好了。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/492033

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX