惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
G
Google Developers Blog
人人都是产品经理
人人都是产品经理
U
Unit 42
爱范儿
爱范儿
Hugging Face - Blog
Hugging Face - Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
WordPress大学
WordPress大学
B
Blog RSS Feed
The Cloudflare Blog
D
Docker
A
About on SuperTechFans
IT之家
IT之家
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Y
Y Combinator Blog
月光博客
月光博客
云风的 BLOG
云风的 BLOG
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
MongoDB | Blog
MongoDB | Blog
Google DeepMind News
Google DeepMind News
The GitHub Blog
The GitHub Blog
博客园_首页
Stack Overflow Blog
Stack Overflow Blog

GeekPlux

一代人的博客,一代人的青春注脚 那些年我打过的日结工 来美国的两年后 2023 一蓑烟雨 在美国拥有一辆 Tesla 的成本 我的 Workspaces 十年进化史 迁移豆瓣读书记录到 goodreads 来美国之后,如何快速安顿下来 疫情三年 与人聊天的美好 我获取信息的方法 2022 版 我是如何学会编程的 2020 恍如隔世 接外包的一些坑和小技巧 论交友 往返香港隔离指南 即将失明,还能继续做程序员吗 小谈我对新技术的态度 远程工作如何提高效率 复式记账、财报、量化与图论 我为什么从阿里巴巴离职 2019 柳暗花明 YouTube 观看历史数据分析 投资被动型指数基金正在造成下一次金融泡沫? 三种主流的网赚套利,躺着赚钱? 色盲的世界 我是如何管理 21 张信用卡的 薅羊毛的最大意义:保持对规则的敏感度 来香港的两个月 数据可视化技术实现的关键点
C++指针与引用小结
GeekPlux · 2013-01-02 · via GeekPlux

概念

引用

引用就是某一对象(变量)的一个别名。举个例子更容易理解:

int m;
int &n = m;

这段程序中,nm的一个引用(reference),m是被引用物(referent)。 n相当于m别名(绰号),对n的任何操作就是对m的操作。 n既不是m的拷贝,也不是指向m的指针,其实n就是m它自己。

指针

指针用于指向对象(变量)。指针提供对其所指对象的间接访问。指针用于指向单个对象。

(这三句摘自《C++ Primer, Fourth Edition》)。再举个例子:

int m;
int *n = &m;

第二条语句定义了一个指向int型的指针n,并初始化n使其指向int型的变量m*n中的 * 操作符表明n是一个指针变量(n本质上也是一个变量,只不过这个变量的存储内容是 地址)。 &m中的 & 符号是取地址操作符,当此操作符用于一个对象上时,返回的是该对象的存储地址。

联系

  • 都是有关地址的概念。 指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。
  • 都是间接访问其他对象。
  • 都能用于函数参数和返回值的传递。

区别

  • 一、指针是一个实体(本质上就是存放变量地址的一个变量),而引用只是个别名。

这句话从内存分配的角度很好理解,程序会为指针变量分配内存区域,而引用不分配内存区域。

  • 二、指针可变,引用不可变。

指针在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。 引用在逻辑上不独立,具有依附性,所以引用必须在一开始就被初始化,而且引用一旦和某个对象绑定后就不能再改变(从一而终)。

  • 三、指针可以为空,引用不能为空。

即指针可以为 NULL,而引用必须与合法的存储单元关联。

  • 四、传递方式不同。

首先,函数参数和返回值的传递方式大概可以理解为三种:

  1. 值传递
  2. 指针传递
  3. 引用传递

先理解值传递:值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个拷贝。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。

指针传递本质上值传递,只不过它所传递的是一个地址值。然后把上面那段话「翻译」一下:指针传递时,形参是一个指针变量,该变量拷贝了实参的地址值,然后作为被调函数的局部变量(传递过来的实参的地址值不会变),再然后我们可以用*操作符访问实参,从而对实参进行操作。很绕口,不如看个例子:

#include <iostream>
using namespace std;

void f(int *n)
{
  *n = 5;
}

int main()
{
  int m = 1;
  cout << m << endl;
  f(&m);
  cout << m << endl;

  return 0;
}

该程序输出结果为:1 5

在函数f中,形参n是指向int型的指针变量,拷贝了实参m的地址,作为f函数的局部变量,然后通过*n改变实参m的值。

而在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址(不必通过*操作符),即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量(此时的形参其实就是实参)。再举个例子:

#include <iostream>
using namespace std;

void f(int &n)
{
  n = 5;
}

int main()
{
  int m = 1;
  cout << m << endl;
  f(m);
  cout << m << endl;

  return 0;
}

输出结果为:1 5

nf中的局部变量,同时n也是实参m的引用。此时n就为m,所以在f中对n的操作都会影响m,为n赋值 5 就是对m赋值 5。

还可以从编译角度理解(此处从网上摘录):

程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值。符号表生成后就不会再改,因此指针可以改变其指向的对象(指针变量中的值可以改),而引用对象则不能修改。

  • 五、引用不能 const,指针能 const,const 的指针不可变。 对于引用,具体指没有 int&const a 这种形式,而 const int& a 是有的, 前者指引用本身(即别名)不可以改变,这是理所当然的,所以不需要这种形式,后者指引用所指的值不可以改变)

  • 六、「sizeof 引用」得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小。

  • 七、指针和引用的自增(++)运算意义不一样(很好理解)。

要理解知识必须先建立知识的结构,就像盖楼房一样,必须先有框架,所以此处附上本人制作的一幅简单的思维导图: