C++中引用和指针有什么区别?(别名与地址的区别)

引用必须初始化且不可重绑定,指针可为空或后期赋值;sizeof和typeid对引用返回所引类型的属性,指针则返回地址相关属性;引用传参避免拷贝但非const引用不接受临时量。

引用必须初始化,指针可以为空

引用在定义时就必须绑定到一个已存在的对象,之后不能再改绑;而指针可以先声明,后赋值,也可以一直不指向任何对象(即为 nullptr)。这是最基础也最容易踩坑的一点:试图使用未初始化的引用会导致编译错误,但未初始化的指针可能只是悬空,运行时才出问题。

  • int x = 42; int& r = x; ✅ 合法,rx 的别名
  • int& r; ❌ 编译失败:引用必须初始化
  • int* p; ✅ 合法,p 是未初始化的指针(值不确定)
  • int* p = nullptr; ✅ 明确置空,安全可判

引用不能重新绑定,指针可以随时改指向

引用一旦初始化,就永远代表那个对象,没有“重新赋值为另一个对象的引用”这回事;而指针变量本身的值(即地址)可以多次修改。很多人误以为 r = y; 是让 r 指向 y,其实这只是给 r 所绑定的对象(即 x)赋值为 y 的值。

int x = 10, y = 20;
int& r = x;
r = y;        // 等价于 x = y; → x 变成 20,r 仍绑定 x
int* p = &x;
p = &y;       // ✅ p 现在指向 y,和之前完全无关

sizeof 和 typeid 行为不同

sizeof 对引用返回的是所引用类型的大小,不是“引用头”的大小;而指针的 sizeof 总是返回地址宽度(如 64 位系统通常是 8)。同样,typeid 对引用返回的是所引用类型的类型信息,不是“引用类型”本身。

  • int x; int& r = x;sizeof(r)sizeof(int),不是 8
  • int* p;sizeof(p) 是平台指针大小(通常 4 或 8)
  • typeid(r).name()typeid(x).name() 一致;typeid(p).name()int*

函数参数传递中,引用避免拷贝但不改变语义

用引用传参(尤其是 const T&)能避免大对象拷贝,又保持调用端语法简洁;而指针传参需要显式取地址(&obj),且调用方得承担空指针检查责任。但要注意:非 const 引用参数强制要求传入左值,这点常被忽略。

void f(const std::string& s) { /* 安全读取 */ }
void g(std::string* s) { /* 必须检查 s != nullptr */ }

std::string s = "hello"; f(s); // ✅ f("abc"); // ✅ 字符串字面量隐式转为 const std::string& g(&s); // ✅ g(nullptr); // ❌ 危险,且调用写法暴露实现细节

真正容易被忽略的是:void h(int& x) 无法接受字面量或临时量(如 h(42) 编译失败),而 const int& 可以——这不是语法糖,是语言对生命周期延长的特殊保证。