C++addressof获取对象真实地址

2026-04-11 09:00:31 877阅读 0评论

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&,且对所有类型(包括 constvolatile、甚至 const volatile)都安全有效


真实踩坑场景,不止教科书里有

  • 调试时地址对不上:你在 GDB 里 p &objp 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 两小时。

文章版权声明:除非注明,否则均为Dark零点博客原创文章,转载或复制请以超链接形式并注明出处。

发表评论

快捷回复: 表情:
验证码
评论列表 (暂无评论,877人围观)

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

目录[+]