news 2026/6/10 13:03:59

跟我学C++中级篇——取地址操作

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
跟我学C++中级篇——取地址操作

一、取地址

在C/C++开发中,指针操作既是一个难点,同时也是一个无法绕开的知识点。一个对象的指针,可以说就是一个对象的地址。那么如何取得这个对象指针呢?或者说如何取得对象地址呢?在传统的开发中,开发者可以通过“&”运算符来获取对象的指针即地址。而在C++11中,开发者又发现了一个std::addressof也可以达到同样的功能。下面将对它们之间的区别和联系进行分析。

二、std::addressof

在C++11中提供了一个接口std::addressof,它的作用与“&”运算符的功能一样也是获取一个对象的实际的地址。看一下其定义:

//定义template<class T>T*addressof(T&arg);//实现template<typename _Tp>_GLIBCXX_NODISCARDinline_GLIBCXX17_CONSTEXPR _Tp*addressof(_Tp&__r)noexcept{returnstd::__addressof(__r);}template<typename _Tp>inline_GLIBCXX_CONSTEXPR _Tp*__addressof(_Tp&__r)_GLIBCXX_NOEXCEPT{return__builtin_addressof(__r);}

__builtin_addressof函数是gcc内置的取地址的函数,它不管&运算符是否被重载都会获取对象的地址。

三、源码

其源实现基于两种场景:

  1. 基于编译器内置函数
    这是非常广泛的一种方法,代码如下:
template<typename _Tp>_GLIBCXX_NODISCARDinline_GLIBCXX17_CONSTEXPR _Tp*addressof(_Tp&__r)noexcept{returnstd::__addressof(__r);}template<typename _Tp>inline_GLIBCXX_CONSTEXPR _Tp*__addressof(_Tp&__r)_GLIBCXX_NOEXCEPT{return__builtin_addressof(__r);}

__builtin_addressof函数是gcc内置的取地址的函数,它不管&运算符是否被重载都会获取对象的地址。
2. 基于非编译器内置函数
简易实现如下:

template<class T>T*addressof(T&arg){return(T*)&(char&)arg;}

正规的实现如下:

template<typename _Tp>inline_Tp*__addressof(_Tp&__r)_GLIBCXX_NOEXCEPT{returnreinterpret_cast<_Tp*>(&const_cast<char&>(reinterpret_cast<constvolatilechar&>(__r)));}template<typename _Tp>inline_Tp*addressof(_Tp&__r)noexcept{returnstd::addressof(__r);}

从简单实现其实可以更好的理解其实现的过程,先是强制转成字符类型的引用(char&),这样做的目的一是防止导致对&运算符的重载操作(char类型作为基础类型没有重载&);二是可以防止出现转换为其它类型(大于1字节)可能导致的内存对齐动作(地址就有可能变化)。然后取地址获取真实的地址即&(char&)中的外面的&(char没有重载&),再强制转成指定类型的指针。
后面的实现,则是为了安全和效率起见,所作的安全控制和内联(inline),说一下const volatile,一个变量既可以是常量又可以可变。这要从两个角度来理解,比如一个寄存器,对程序而言它是常量,不应该尝试去改变它。但对硬件而言,它可能被改变。它的目的是为了防止编译器优化,确保获取准确的地址。而上面的const_cast则去除刚刚增加的const volatile。其它就非常好理解了。

四、应用场景和限制

既然有&可以获取地址,为什么又要造出一个std::addressof。大家都知道,除了一些特定的运算符不可以重载的话,大多数的运算符是可以被重载的,其中就包含&运算符。那么问题来了,在被重载了&运算符的对象中如果还是用&来取地址的话,会是什么情况呢?看下面的例程:

#include<iostream>#include<memory>structDemo{int*operator&(){return&b_;}int*getaddr(){return&a_;}inta_;intb_;};intmain(){Demo d;std::cout<<"d address:"<<&d<<",d.a_ address:"<<d.getaddr()<<std::endl;std::cout<<" addressof get addr:"<<std::addressof(d)<<std::endl;}

即使不运行代码也可以看出来,直接使用&获取的地址一定是有问题的。所以通过上面的代码可以总结出来,&和std::addressof的不同主要在于对重载&的应用的不同。后者可以始终正确的获取对象的地址。或者可以这样理解,在没有重载&运算符的情况下,二者的功能是一致的,编译器会将其优化化相同的代码。不过,std::addressof不能用于不完整类型,且在C++17前不允许用于右值。
std::addressof理论是在可以排除前面的限制情况下都可以使用。但有几个典型的应用场景:

  1. 在模板编程中必须使用
  2. &重载的情况下
  3. 确保获取准确的地址需求中
  4. 在处理无法主动控制的库或类型时

&的优势就在于代码简洁,清晰明了。所以如何选择二者,就看开发者对实际代码的了解程度了。在非上面几个典型的应用场景下,可以大胆的使用。能省一点是一点嘛。

五、例程

看一个cppreference上的例子:

#include<iostream>#include<memory>template<class T>structPtr{T*data;Ptr(T*arg):data(arg){}~Ptr(){delete data;}T**operator&(){return&data;}};template<class T>voidf(Ptr<T>*p){std::cout<<"Ptr overload called with p = "<<p<<'\n';}voidf(int**p){std::cout<<"int** overload called with p = "<<p<<'\n';}intmain(){Ptr<int>p(newint(42));f(&p);// calls int** overloadf(std::addressof(p));// calls Ptr<int>* overload}

上面的代表显示的结果应该是一致的才对。很好理解,第一个打印虽然重载的&运算符返回二级指针,但实际调用也是一个二级指针的对象转换,再加上&本身的取地址,恰恰是一级指针。

六、总结

任何技术不可能都是面面俱到的,新技术的出现,有的是颠覆性的修改而有的则是完善性的修改。std::addressof其实可以理解为对&的一种完善,它把一些可能出现的漏洞以及在发现这些漏洞后可能导致的代码复杂性做了统一的处理。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/5 21:21:10

百度世界大会合作伙伴招募:联合发布行业解决方案

VibeThinker-1.5B-APP&#xff1a;小模型如何实现高强度推理突破&#xff1f; 在当前AI技术飞速演进的背景下&#xff0c;大语言模型的发展路径正悄然发生转变。曾经“参数为王”的时代&#xff0c;正在被一种更务实、更高效的新范式所挑战——用更少的参数&#xff0c;完成更强…

作者头像 李华
网站建设 2026/6/5 9:50:56

大模型开发者必收藏:2025年LLM技术进展与未来趋势深度解析

本文全面回顾2025年大语言模型发展&#xff0c;重点介绍了推理模型崛起、RLVR与GRPO算法成为主流训练方法、架构向高效混合模型转变、推理侧缩放与工具调用广泛应用。作者探讨了AI在编程、写作与科研中的应用价值&#xff0c;强调私有数据作为企业竞争优势的重要性&#xff0c;…

作者头像 李华
网站建设 2026/5/29 19:30:15

JS 数组魔法:map 和 filter 怎么用?

JS 数组魔法&#xff1a;map 和 filter 怎么用&#xff1f;生活中的例子 01电商网站&#xff1a;把所有商品价格打 8 折显示&#xff08;map&#xff09;。生活中的例子 02社交软件&#xff1a;只显示在线的好友列表&#xff08;filter&#xff09;。生活中的例子 03待办清单&a…

作者头像 李华
网站建设 2026/5/28 8:16:20

【收藏】关于ReAct Agent的深入理解——ReAct Agent是稳定的吗?

“大模型的原生能力存在边界&#xff0c;当智能体需攻克复杂任务时&#xff0c;我们必须通过精准提示词&#xff0c;为模型注入复杂任务的标准化处理逻辑。” 近期在深耕模型部署实践与Langchain新版本框架适配时&#xff0c;一个关于ReAct Agent智能体的核心问题突然浮现脑海&…

作者头像 李华