总是引用的地址等于源地址
Is always the address of a reference equal to the address of origin?
这看起来是一个非常基本的主题,但对我来说非常重要
以下示例显示引用变量的地址等于原始变量的地址。我知道这就是我们可以从C/C++的概念中期待的。但是,是否总是保证这些地址在任何情况下都是相等的?
#include <iostream>
#include <vector>
#include <string>
class Point
{
public:
double x,y;
};
void func(Point &p)
{
std::cout<<"&p="<<&p<<std::endl;
}
int main()
{
std::vector<Point> plist;
plist.push_back({1.0,4.0});
plist.push_back({10.0,20.0});
plist.push_back({3.0,5.0});
plist.push_back({7.0,0.4});
std::cout<<"&plist[2]="<<&plist[2]<<std::endl;
func(plist[2]);
return 0;
}
结果:
&plist[2]=0x119bc90
&p=0x119bc90
不幸的是,许多人混淆了C++引用的逻辑和物理含义。
逻辑级别
关于C++引用,我发现了一件至关重要的事情:
一旦初始化了引用,它就会变得发音不清。我所说的"发音不清"的意思是,当你在运行时可执行代码中命名引用时,总是——你会自动获得它引用的变量,所以你无法触摸引用本身。引用只是变量的替代名称(别名)。
因此,如果你有类似int i_var = 10; int& i_ref = i;
的东西,无论你用它形成什么可执行表达式,任何提到i_ref
的东西实际上都意味着i_var
。
此外,有些人认为将引用视为"自取消引用指针"是有帮助的。假设你有一个指针int* p
,但每次你把它称为p
时,你实际上是指*p
。例如,p = 10
表示*p = 10
,&p
表示&(*p)
——p
也指向的int
的地址。这就是引用的逻辑工作方式。
它也适用于您的代码。只要你有Point &p = plist[2];
(当您调用func(plist[2])
时发生),然后p
和plist[2]
开始引用同一个东西——plist
中由索引2存储的某个Point
对象。所以现在CCD_ 19和CCD_。
键入系统级
如果您注意到,我使用了术语"运行时可执行代码"或"可执行表达式"。让我澄清一下
编译器实际上知道a
和b
之间的区别:
int a = 0;
int& b = a;
std::cout << std::boolalpha
<< std::is_same_v<decltype(a), decltype(b)>; // -> false
如您所见,a
和b
类型不同。然而,std::is_same_v<decltype(a), decltype(b)>
是在编译时计算的,所以我不认为它是"可执行表达式"。
物理级别
请注意,到目前为止,我没有说过引用的地址和被引用变量的地址相同。为什么?因为如果你从逻辑上思考-,它们就不是。
引用必须以某种方式实现,不管你喜欢与否。我相信,在i_var
和i_ref
编译器的例子中,只需将所有i_ref
替换为i_var
,"引用"的任何物理表示都将永远不存在。另一方面,如果您将引用存储在类中,那么它很可能是用指针实现的
尽管实现依赖于编译器,但如果引用实际上是隐藏的指针,则该指针的地址和它所指向的对象的地址显然是不同的。
然而,你为什么要在意呢?你永远不会知道推荐人的地址!在任何可执行表达式中,当您说i_ref
时,表示i_var
,还记得吗?:)
好吧,如果你真的很好奇"引用的地址是什么",有一种情况你可以弄清楚——当引用是类的成员时:
int main()
{
int var = 10;
int& real_ref = var;
struct { int& ref; } fake_ref = { var };
std::cout << &var << std::endl; // address of var
std::cout << &real_ref << std::endl; // still address of var
std::cout << &fake_ref << std::endl; // address of reference to var
std::cout << sizeof var << std::endl; // size of var
std::cout << sizeof real_ref << std::endl; // still size of var
std::cout << sizeof fake_ref << std::endl; // size of reference to var
return 0;
}
x64编译器上的输出:
000000A9272FFBA4 <- same
000000A9272FFBA4 <- same
000000A9272FFBC0 <- different
4 <- same
4 <- same
8 <- different (8 on 64 bit and 4 on 32 bit compiler)
引用的地址总是等于源地址吗?
对象有地址。引用不是对象,也不一定有地址。当运算符的地址应用于引用时,结果是被引用对象的地址。
在任何情况下都能保证这些地址相等吗?
是的,它是有保证的(除非运算符的地址被一个愚蠢的实现重载)。
被引用引用的对象与该引用引用所引用的对象是同一对象。一个对象的地址与该对象的地址相同。。。因为它是那个对象:)
现在,可以重载addressof运算符,使其不再返回该对象的实际地址。在这种情况下,这两个调用可能会导致不同的地址。演示:
struct evil {
static evil silly;
static bool insane;
evil* operator&() {
evil* bonkers = insane ? std::addressof(silly) : this;
insane = !insane; // does this mean it is no longer insane?
return bonkers;
}
};
bool evil::insane = true;
foo evil::silly;
int main() {
evil e;
evil& ref = e;
std::cout << &e << 'n';
std::cout << &ref << 'n';
std::cout << &ref << 'n';
}
可能输出:
0x7ffffbeef42d
0x600dd1
0x7ffffbeef42d
如果引用的类型和用于初始化它的变量的类型不同,则引用的地址可以与初始化变量的地址不同。
例如
#include <stdio.h>
struct Class1
{
int x;
};
struct Class2
{
int p;
};
struct Class12 : Class1, Class2 {};
int main(int argc, char* argv[])
{
Class12 p;
Class1& r1 = p;
Class2& r2 = p;
printf("p=%p, r1=%p, r2=%p", &p, &r1, &r2);
}
运行时,输出为:
p=0x7ffefcfd1a20, r1=0x7ffefcfd1a20, r2=0x7ffefcfd1a24
r1的地址与r2不同,尽管这两个引用是从同一个变量初始化的。
在任何情况下都能保证这些地址相等吗
是的,您正在传递该对象的引用,因此不会进行复制(如果这是问题的背景),那么您正在打印的正是调用函数时原始参数的地址。
在任何情况下都能保证这些地址相等吗
根据C++标准,未指定引用是否需要存储。另一方面,它还说,取一个参考的地址会给你参考对象的地址。因此,即使编译器选择在内部为引用而不是引用有一个单独的存储,也可以向程序员保证它们的地址是相同的。
- 将数组的地址分配给变量并删除
- 空基优化子对象的地址
- C++ 指针的内存地址和指向数组的内存地址如何相同?
- 在C++中打印指向不同基元数据类型的指针的内存地址
- 如何在c++程序中找到函数的地址
- OMNET++:如何在INET 4.0中获取帧的源MAC地址?
- 总是引用的地址等于源地址
- 如何确定 cudaErrorIllegalAddress 是由于目标地址还是源地址
- 阅读UDP广播的源地址
- 如何使用 Poco::Net::HTTPSClientSession 绑定特定的源 IP 地址
- 可以通过堆分配的地址地址静态变量匹配的值地址值
- 为什么函数的地址(地址的下部字)在每次执行中总是相同的
- 获取传入连接的源 IP 地址
- 如何将源IP地址设置为0.0.0.0
- 在 Boost ASIO 中,如何设置源 IP 地址以模拟其他服务器的 IP 地址?
- Solaris 10 -- 从 recvmsg() 的 UDP 组播数据包中获取 IP 源地址
- reinterpret_cast保证不更改源指针的地址吗?
- Windows下如何设置UDP源地址
- 套接字,Unix域UDP c++ recvfrom无法填充源地址
- 从路由获取源地址