改变一个值的引用为何原值不变?

1

如题,代码很简单,一看便知。

环境:win10, vs2019, dev-c++。

该程序旨在测试 cons_cast,预期结果不符后,测试了相关地址,发现是一样的,为何结果不同?

预期结果:

2 2 3 3 3

测试结果:

1 2 1 3 3

代码如下:

#include <iostream>
using namespace std;

int main()
{
	const int y1 = 1;
	const int& y2 = y1;
	int& y3 = const_cast<int&>(y1);
	y3 = 2;
	cout << y1 << " ";		// num y1
	cout << y3 << " ";		// num y3

	int& y4 = const_cast<int&>(y2);
	y4 = 3;
	cout << y1 << " ";		// num y1
	cout << y3 << " ";		// num y3
	cout << y4 << endl;		// num y4

	cout << (void*)&y1 << endl;
	cout << (void*)&y2 << endl;
	cout << (void*)&y3 << endl;
	cout << (void*)&y4 << endl;

	return 0;
}
ava
Teternity

2019-11-21

4

我先总结下你的疑惑(希望我的总结没错):

  1. y1(const) 与 y3 地址相同,修改了 y3 的值,y1 的值却不改变;
  2. y2(const) 与 y4 地址相同,修改了 y4 的值,y2 的值却改变了。

这个问题说来话长(以下内容仅针对 C++ 来说)。

C++ 对 const 变量的处理分很多种情况,定义位置、方式、数据类型等,都会影响。例如你的情况:

const int y1 = 1;	// 编译器会将 y1 放到符号表中,使用 y1 时直接查表取值
const int& y2 = y1;	// 编译器会给 y2 分配内存,与普通变量类似,只是多了 const 修饰符

所以,y3 是不可能影响 y1 的值的。

为了详述以上过程,我用汇编代码来解释一下。在 VS2019 下以 [x86|Debug] 成功编译代码,按 F10 进入调试状态,点菜单 Debug -> Windows -> Disassembly(或按 Alt + 8),查看汇编指令。

定义的区别:

		const int y1 = 1;
00B11838	mov		dword ptr [y1],1
		const int& y2 = y1;
00B1183F	lea		eax,[y1]
00B11842	mov		dword ptr [y2],eax
		int& y3 = const_cast<int&>(y1);
00B11845	lea		eax,[y1]
00B11848	mov		dword ptr [y3],eax
		int& y4 = const_cast<int&>(y2);
00B1184B	mov		eax,dword ptr [y2]
00B1184E	mov		dword ptr [y4],eax

那么问题来了:既然分配了内存,为什么输出 y1 时的值与 y3 不同呢?

因为符号表里面有 y1 啊。还是来看汇编吧(我写了另一段简单的代码):

		int i;
		i = y1;
003B187B	mov		dword ptr [i],1
		i = y3;
003B1882	mov		eax,dword ptr [y3]
003B1885	mov		ecx,dword ptr [eax]
003B1887	mov		dword ptr [i],ecx

也就是说,使用 y1 的地方,从来都不用 [y1],而是从符号表里面找到 y1 然后直接使用对应的值。

那么直接使用 [y1] 会怎样呢?

我用汇编实现了一下这个奇怪的需求(我删掉了无关代码):

#include <stdio.h>

int main()
{
	const int y1 = 1;
	int& y3 = const_cast<int&>(y1);

	y3 = 2;

	int i;

	// 对照:普通赋值
	i = y1;
	printf("i = %d\n", i);

	// 对照:汇编赋值实现 i = y1;
	__asm {
		lea		eax, [y1]
		mov		ecx, dword ptr[eax]
		mov		dword ptr[i], ecx
	}
	printf("i = %d\n", i);

	return 0;
}

输出内容:

i = 1
i = 2

于是,终于输出了内存里 y1 地址的真实值,而不是符号表里面记录的值。

PS:不管基于什么理由,我个人都不建议使用 const_cast。

ava
慢羊羊

2019-11-21

多谢村长,只是测试用,一般不会使用,感谢指导 - Teternity 2019-11-22