C++addressof获取对象真实地址
std::addressof:当 &obj 不再可靠时,你得靠它拿真实地址
写 C++ 时,我们习惯性地用 &obj 获取对象地址。这几乎成了肌肉记忆——就像泡面撕开包装、倒水、盖盖子,三秒完成。但有天你突然发现:&obj 返回的地址,居然和对象在内存里真正待的地方不一样?
不是编译器抽风,也不是你眼花了。是有人重载了 operator&。
重载 operator&:合法,但危险
C++ 允许类自己定义取地址操作符。比如:
struct Weird {
int x = 42;
Weird* operator&() { return nullptr; } // 故意返回空指针
};
这时候 &Weird{} 得到的是 nullptr,而对象本身明明刚在栈上分配好,地址真实存在。
更隐蔽的是某些智能指针或代理类(如 std::reference_wrapper)也常重载它,目的可能是屏蔽底层地址、实现透明代理,或者……干脆就是历史包袱。
问题来了:你想做内存拷贝、调试定位、或和 C API 交互(比如传给 memcpy 或 OpenGL 的 glVertexAttribPointer),需要的是对象物理内存地址,不是被“修饰过”的逻辑地址。
这时 &obj 就不可信了。
std::addressof 是什么?不是语法糖,是兜底方案
它不调用 operator&,而是用模板 + 引用折叠 + reinterpret_cast 组合拳,绕过一切用户重载,直取对象在内存中的起始字节位置。
标准写法就一行:
#include <memory>
auto ptr = std::addressof(obj);
它底层大致等价于:
template<class T>
constexpr T* addressof(T& arg) noexcept {
return reinterpret_cast<T*>(
&const_cast<char&>(reinterpret_cast<const volatile char&>(arg))
);
}
别被这一长串吓住——重点不在怎么实现,而在它不依赖 operator&,且对所有类型(包括 const、volatile、甚至 const volatile)都安全有效。
真实踩坑场景,不止教科书里有
-
调试时地址对不上:你在 GDB 里
p &obj和p std::addressof(obj)输出两个值,第一反应是“我编译器坏了”,其实是obj所在类悄悄重载了operator&,调试器老老实实用了重载版本。 -
用
memcpy做 POD 类型序列化失败:你写了memcpy(buf, &obj, sizeof(obj)),结果解包后字段全乱。查到最后发现obj是某个封装类实例,&obj返回的是内部缓冲区偏移指针,而非对象头地址。 -
对接 C 接口出错:比如
std::vector<char> buf; fwrite(buf.data(), 1, n, fp);没问题;但若换成MyBufferWrapper b; fwrite(&b, 1, sizeof(b), fp);——一旦MyBufferWrapper重载了operator&,你就把一串无意义指针或垃圾内存塞进了文件。
这些都不是假设。我在重构一个音视频 SDK 时,就遇到某 AudioFrame 类为支持“零拷贝视图”重载了 operator&,结果旧代码里所有 &frame 都失效,avcodec_send_frame 直接崩溃。改用 std::addressof(frame) 后,问题当场消失。
它不是万能钥匙,但该用时别犹豫
std::addressof 只解决一个问题:拿到对象在内存中真实的、可寻址的起始位置。
它不负责对象是否存活、是否对齐、是否可读写——那是你自己的责任。
几个关键边界要心里有数:
-
不能用于临时对象的地址长期保存:
std::addressof(std::string{"hello"})拿到的是临时string的地址,但该对象生命周期只到当前表达式结束。这不是addressof的错,是 C++ 对象生命周期规则。 -
对位域(bit-field)成员无效:
std::addressof(obj.bit_field_member)编译不过。位域没有独立地址,这是语言限制,addressof也无能为力。 -
不要滥用在普通场景:日常遍历
vector、传参、写shared_ptr构造,完全不需要它。它存在的意义,是当你明确怀疑&被污染时,提供一条干净路径。
怎么判断该不该用?
一个简单心法:
如果你写的代码需要和内存布局强相关(序列化、二进制协议、C 互操作、自定义分配器、调试工具),而对象类型又来自第三方库或团队内封装类——先查文档,再看源码,最后加个 static_assert(!std::is_same_v<decltype(&obj), decltype(std::addressof(obj))>, "重载了operator&!")。
比猜强,比崩溃早。
std::addressof 不炫技,不抢镜,但它像一把藏在工具箱角落的平口螺丝刀:平时看不见,关键时刻拧紧最后一颗松动的螺丝。
C++ 的强大,在于它允许你精细控制;它的诚实,也在于它从不隐藏代价——重载 operator& 是自由,而 std::addressof 是自由之后的守门人。
下次 &obj 让你心里咯噔一下,别急着翻 Stack Overflow,试试 std::addressof(obj)。
那行代码不会让你变资深,但会让你少 debug 两小时。


还没有评论,来说两句吧...