为什么不能将两个地址加在一起?
假设我定义了两个变量x和y:
int x = 5;
int y = 6;
我可以x通过以下方式获取存储地址:
cout << &x << endl; // 0x61fe18
这是一个十六进制数。现在,如果我想在加上 5 后打印这个数字,这很简单:
cout << &x + 5 << endl; // 0x61fe1d
但是,如果我总结的地址一起x和y:
cout << &x + &y << endl;
我收到一个错误:
invalid operands of types 'int*' and 'int*' to binary 'operator+' cout << &x + &y << endl; ~~~^~~~
为什么我不能添加这两个地址?
回答
简短回答:因为 C++ 标准是这样说的。
长答案:因为它没有意义。永远不会出现将两个地址加在一起会产生任何有用的情况。
当您添加(或减去)地址的整数偏移量时,您会到达位于地址给定偏移量处的对象。非常有用,基本上是所有数组操作的基础。
但是如果你把两个地址加在一起,我们就会到达内存中一个完全随机的位置,在那里你不可能找到任何有价值的东西。因此,语言不允许您这样做,并且编译器拒绝编译此代码。
但是,如果您只需要两个地址的数字表示之和的数字表示(例如,对于复杂的运行时混淆),则可以使用专门为此设计的类型:uintptr_t. 以下是有效代码:
auto z = uintptr_t(&x) + uintptr_t(&y);
回答
将某物转换为整数的能力并不意味着它是一个整数。
计算机使用二进制逻辑,您可以将任何二进制信息转换为整数。但是作为一个整数,就意味着加减乘除都有预期的意义。
指针存储为二进制数据,但许多操作对它们没有意义。
我在地球上的位置可以表示为一对经纬度数字。那对数字不是我在地球上的位置。
将数字相加是一个合理的操作。将位置加在一起是无稽之谈。
“纽约市的位置”加上“莫斯科的位置”没有意义。
“纽约的纬度”加上“莫斯科的纬度”是一个数字,但那个数字基本上是无稽之谈,纽约和莫斯科的纬度/经度值之和的纬度/经度值对同样如此。
纽约和莫斯科之间的中点确实有道理,但有两个(世界的对面)。此外,纽约市和莫斯科之间 25% 的路程是有道理的。
接下来“从纽约市到莫斯科的最短路径”是一个向量。它不是一个位置,而是地球表面的一个方向。你可以把这个向量添加到另一个位置——“如果我从多伦多开始呢”——然后得到一个新的位置。
指针是位置,如“NYC”和“Moscow”。当您添加到指针时,该整数将充当指针位置空间上的向量。
C++ 试图让无意义的操作不会发生,除非你强迫它。添加指针——添加位置——是无稽之谈。将整数添加到指针是有意义的。取两个指针之间的差异是有道理的。
当你打印出一个指针,编译器使用void*的过载std::ostream& << void*。此重载用于调试目的,并将指针的位解释为十六进制整数。
您可以通过将指针转换为 a 来手动执行此操作std::uintptr_t- 一种无符号整数类型,保证足够大以存储指针的位。
你会注意到,当你这样做时&x + 5,你得到的整数并不比你这样做时大 5 &x。其实就是&x+sizeof(x)*5。
这是因为指针算术假设您正在处理一个数组,并且正在向前/向后移动一个对象大小。
std::cout << &x << std::endl; // 0x61fe18
std::cout << (&x + 5) << std::endl; // 0x61fe1d
只有发生,如果x有sizeof(x)==1。正如您声称x的那样int,这在 C++ 中是不可能的。
如果sizeof(int)是4(现在很常见),
std::cout << &x << std::endl; // 0x61fe18
std::cout << (&x + 5) << std::endl; // 0x61fe2c
是你会得到的。
这是因为,同样,指针不是整数。能够将它们打印为整数并不意味着它们是整数。