☰
C++引用和指针的区别你知道吗
2026/11/6 5:48:21 网站建设 项目流程

前言


引用和指针都能「间接访问另一个对象」,于是很多人把它们当成同一种东西的两种写法:int&就是不能为空的int*,*p就是r。这个理解在简单函数传参时勉强够用,但一旦涉及重新绑定、对象生命周期、const限定和重载决议,就会出错。


最核心的差别有两条。第一,引用必须在定义时绑定到一个已存在的对象,之后不能重新绑定;对引用赋值是给被引用的对象赋值,不是让引用改指向别处。第二,引用不是独立对象,标准不要求它有地址、不要求它占存储;sizeof一个引用得到的是被引用类型的大小,而sizeof指针得到的是指针本身的大小。


本文从语法、语义、const、函数参数和生命周期五个方面对比两者,并给出选择建议。示例以 C++17 为标准,不涉及编译器实现细节的断言。


一、绑定与重新绑定


// 适用于 C++17
#include <iostream>

int main() {
int a = 10;
int b = 20;

int& ref = a; // ref 绑定到 a,且只能绑定这一次
ref = b; // 把 b 的值赋给 a,a 变成 20;ref 仍绑定 a

int* ptr = &a; // ptr 指向 a
ptr = &b; // ptr 改为指向 b,a 不受影响

std::cout << a << ' ' << b << ' ' << *ptr << '\n';
return 0;
}

按标准语义,a变成 20,b仍是 20,*ptr是 20。更关键的是地址:&ref与&a相同,且在执行ref = b之后仍然相同;ptr的值则从&a变成了&b。引用没有「改指向」这个操作,这是它与指针最本质的区别。


引用在声明时必须初始化,int& r;无法通过编译。指针可以先声明后赋值,也可以是空指针。引用不存在合法的「空引用」,即使通过解引用空指针来初始化引用,也是未定义行为(undefined behavior, UB):


// 适用于 C++17
int* p = nullptr;
int& r = *p; // UB:不能形成对空对象的引用

二、sizeof、地址与数组




表达式结果



sizeof(ref)被引用类型的大小,例如sizeof(int)

sizeof(ptr)指针类型的大小,通常与平台字长相关

&ref被引用对象的地址

&ptr指针变量自身的地址

ref + 1被引用对象的值加一(若支持)

ptr + 1指向下一个同类型元素(要求指向数组内)



引用不是数组元素类型的好选择:不能定义引用的数组(int& arr[3]非法),但可以定义数组的引用(int (&arr)[3])。指针数组和指向数组的指针则是常见类型。函数参数写成int arr[]会退化为int*,而写成int (&arr)[3]能保留长度信息;这也是模板推导数组长度时常用的技巧。


对引用做++、--、+等运算,实际作用于被引用的对象;对指针做这些运算,改变的是指针值(或对指向对象做运算后再移动)。指针算术只允许在同一个数组(含尾后位置)内进行,越界解引用是 UB。


三、const 的四种组合


const与指针结合时,const在*左边修饰「被指对象」,在右边修饰「指针本身」:




写法含义能否改指向能否改对象



int* p普通指针能能

const int* p指向 const int 的指针能不能

int* const pconst 指针不能能

const int* const p两者都 const不能不能



引用没有「引用本身 const」的说法。const int& r表示「不能通过 r 修改被引用对象」,被引用对象本身可能不是 const;它也可以绑定到临时对象,并延长该临时对象的生命周期到引用的作用域结束。这一点让const T&成为只读大对象参数的首选。


// 适用于 C++17
#include <string>

void print(const std::string& text); // 只读,不拷贝,可接临时对象
void modify(std::string& text); // 可修改,不能接临时对象

int main() {
print("temporary"); // 合法
// modify("temporary"); // 非法:非 const 左值引用不能绑定临时对象
std::string s = "hello";
modify(s); // 合法
return 0;
}

四、函数接口与多态


传引用和传指针都能避免拷贝并允许函数修改实参,但语义不同:




维度T&T*



是否可为空否是

是否必须初始化是否

调用语法直接传对象需要取地址

函数内访问像普通对象需要解引用

能否改指向否是

表达「可选」不自然自然

表达「必选」自然需要额外检查



选择时看语义:参数一定存在且需要修改,用T&;参数可能缺失,用T*或std::optional<T>;只读大对象,用const T&;需要转移所有权,用智能指针或按值移动。


返回值方面,返回引用可以避免拷贝,但绝不能返回局部变量的引用。局部变量在函数返回时销毁,引用立即悬垂。返回指针同理。可以安全返回的引用包括:静态存储期对象、调用方传入的对象、成员变量(前提是对象生命周期覆盖使用期)。


多态与重载


通过基类指针或引用调用虚函数都能实现动态绑定;按值传递会发生对象切片。引用在语法上更接近值语义,指针则更适合表达「可能为空」或「需要重新指向」。


重载决议也会区分引用和指针:void f(int&)与void f(int*)是两个不同重载,f(x)和f(&x)分别匹配。const T&与T&也能构成重载,但传入非 const 左值时优先匹配T&,传入 const 对象或临时对象时只能匹配const T&。


常见坑点



  1. ❌ 返回局部变量的引用。


// 适用于 C++17
int& bad() {
int x = 1;
return x; // 返回后 x 已销毁,悬垂引用
}

✅ 按值返回,或返回生命周期足够长的对象。


// 适用于 C++17
int good() { return 1; }


  1. ❌ 认为ref = other会让引用改绑。


// 适用于 C++17
int a = 1, b = 2;
int& r = a;
r = b; // 改的是 a 的值,r 仍绑定 a

✅ 需要改指向就用指针。


// 适用于 C++17
int* p = &a;
p = &b;


  1. ❌ 混淆const int*与int* const。


// 适用于 C++17
const int* p = &a;
*p = 3; // 编译错误:被指对象是 const
p = &b; // 合法:指针本身可改

✅ 按「const 在 * 左边修饰对象,右边修饰指针」记忆。


// 适用于 C++17
int* const q = &a; // q 不能改指向,但可以 *q = 3


  1. ❌ 用空指针初始化引用。


// 适用于 C++17
int* p = nullptr;
int& r = *p; // UB

✅ 引用必须绑定有效对象;可空语义用指针表达。


// 适用于 C++17
int* p = nullptr;
if (p != nullptr) { int& r = *p; /* ... */ }


  1. ❌ 对非数组指针做算术。


// 适用于 C++17
int x = 1;
int* p = &x;
++p; // 指向不存在的对象,解引用即 UB

✅ 只在数组范围内做指针算术。


// 适用于 C++17
int arr[3] = {1, 2, 3};
int* p = arr;
++p; // 指向 arr[1]


  1. ❌ 让引用成员阻止默认赋值。


// 适用于 C++17
struct S {
int& ref;
};
S a{x}, b{y};
a = b; // 引用成员使默认赋值被删除

✅ 需要可赋值就不要用引用成员,改用指针或值成员。


// 适用于 C++17
struct S { int* ptr; };


  1. ❌ 返回c_str()后继续修改字符串。


// 适用于 C++17
const char* p = s.c_str();
s += "more"; // 可能导致缓冲区重新分配
std::cout << p; // p 可能悬垂

✅ 需要长期保存时复制到std::string。


// 适用于 C++17
std::string copy = s;


  1. ❌ 认为引用一定占内存、一定和指针一样大。


// 适用于 C++17
static_assert(sizeof(int&) == sizeof(int*)); // 不成立的假设

✅ 只依赖标准语义,不依赖引用的布局;sizeof(ref)按被引用类型计算。


总结




维度引用指针



必须初始化是否

可为空否是

可否改绑否是

sizeof被引用类型大小指针大小

算术运算作用于被引用对象改变指针值

常见用途参数、返回值、运算符重载可选参数、动态对象、数组遍历

生命周期风险悬垂引用悬垂指针、空指针



引用和指针不是替代关系,而是表达不同意图的工具:引用强调「就是它,不能为空,不会换」,指针强调「指向某个位置,可以为空,可以换」。把意图写进类型里,接口会比注释更可靠。




需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询