当前位置: 首页 > news >正文

c++中“”符号代表引用还是取内存地址?

c++中,“&”符号有时代表引用,有时代表取地址符。

一、引用和取址

引用是一个已存在变量的别名,修改别名的值,原始变量的值也会改变;而取地址符则是得到一个指针,该指针指向变量的内存地址。

1)引用

int a = 10;
int &ref = a;  // ref 是 a 的引用

此时 ref 和 a 指向的是同一个内存地址,修改 ref 就等于修改 a。

引用也可以作为参数传递:

void func(int &x) {x = 20;  // 修改的是外部传入的变量
}int main() {int a = 10;func(a);  // 这里传进去的是 a 的引用std::cout << a << std::endl;  // 输出 20
}

在函数中修改,函数外部的原始变量的值也相应改变。

2)取地址符

int a = 10;
int *p = &a;  // p 是指向 a 的指针

我之所以会探究这个问题,是因为我原先一直认为,&就是取址符号,而不知道有所谓引用,怀疑一个变量,如果经过多次取址,会出现问题,比如:

void funcA(int &x) {x *= 2;  // 修改的是外部传入的变量
}
void funcB(int &x){x = funcA(x);x += 10;
}int main() {int a = 10;funcB(a);  // 这里传进去的是 a 的引用std::cout << a << std::endl;  // 输出 30
}

以上代码,我就担心,在funcB中,传入了x的内存地址,然后在funcB中调用了funcA,又取一次地址,那么funcA中处理的x,其实是地址的地址?其实这都是误解。在函数中,它们都是引用,不会出现拿到的是“地址的地址”这种局面。

二、引用和取址的使用区别

在这里插入图片描述
在变量定义/函数参数中 → 是引用
在表达式中对变量使用 → 是取地址
在这里插入图片描述

三、为什么要有引用

1、便利和简洁

使用指针可以达到修改原始变量值的目的,但引用带来了额外的便利性和安全性。对比一下:
1)引用

void increment(int &x) {x++;
}int a = 5;
increment(a); // 直接传递 a 的引用,无需 &a

2)指针

void increment(int *x) {(*x)++;
}int a = 5;
increment(&a); // 需要传递 a 的地址

2、安全性

引用必须初始化:在声明引用时,你必须同时初始化它指向哪个变量,这意味着引用总是有效的(除非引用的是一个临时对象)。一旦一个引用被初始化为指向某个变量,它就不能再指向另一个不同的变量。

指针可以不初始化:指针可以在声明时不初始化,这可能导致悬挂指针或未定义行为的问题。

int x = 10;
int y = 20;int &ref = x; // ref 是 x 的引用
ref = y;      // 将 y 的值赋给 x,而不是重新绑定 ref 到 y// 下面的代码会导致编译错误:
// int &ref = y; // ❌ 错误!不能再次初始化一个已经存在的引用

3、避免空指针问题

引用不能为 NULL:引用总是关联到一个有效的对象,因此不会出现空引用的情况,减少了潜在的错误源。
指针可以是 NULL:如果忘记检查指针是否为 NULL 就使用它,可能会导致程序崩溃或其他不可预测的行为。

4、 简化函数重载

在某些情况下,引用可以帮助更清晰地区分函数重载版本。例如,对于接受左值和右值的不同版本的函数,引用可以用来区分这两种情况。

void foo(const std::string &s); // 接受左值
void foo(std::string &&s);      // 接受右值

所谓的左值,就是有明确内存地址的变量的值;而右值则是没有明确内存地址的值,比如运算结果,孤零零的字符串,数值等。右值可以用&&符号来代表(在这里不是 与 符号)。

int x = 10;
int &ref1 = x;
int &&ref2 = 20; // 合法:20 是一个右值,ref 是它的右值引用
int &&ref3 = x;//错!!!x 是左值,不能绑定到右值引用

另一个例子

#include <iostream>
#include <string>// 接受左值:const 左值引用
void foo(const std::string &s) {std::cout << "[左值版本] 接收到一个左值: " << s << std::endl;
}// 接受右值:右值引用
void foo(std::string &&s) {std::cout << "[右值版本] 接收到一个右值: " << s << std::endl;// 可以在这里安全地移动 s 的资源std::string movedStr = std::move(s);std::cout << "[右值版本] 资源转移后: " << movedStr << std::endl;
}int main() {std::string str = "Hello";// 调用左值版本foo(str); // 调用右值版本(临时对象)foo("World");// 调用右值版本(显式 move)foo(std::move(str));// 此时 str 已经被 move,内容不确定,但仍是有效状态if (str.empty())std::cout << "str is now empty after move" << std::endl;return 0;
}

5、其他

使用引用而不是按值传递可以避免不必要的对象拷贝。对于大型对象如 std::string,这可以显著提高性能,因为不需要复制整个字符串的内容。

void foo(const std::string &s) {// s[0] = 'A'; // ❌ 错误:不能修改 const 对象std::cout << s << std::endl; // ✅ 合法:只读访问
}
std::string str = "Hello";
foo(str); // 传递的是 str 的引用,而不是它的副本

相关文章:

  • 手写 Vue 源码 === 完善依赖追踪与触发更新
  • 数组和集合
  • 【CSS】Grid 的 auto-fill 和 auto-fit 内容自适应
  • NHANES指标推荐:AISI
  • Qwen2-VL详解
  • cocos中加入protobuf和编译protobuf的方法
  • 软件设计师2025
  • SecureCRT SFTP命令详解与实战
  • Unity3D 游戏内存优化策略
  • 模拟设计中如何减小失配
  • 淘宝按图搜索商品(拍立淘)Java 爬虫实战指南
  • 罗氏线圈抗干扰特性测试方法研究
  • 霍尔传感器与罗氏线圈的对比分析
  • [D1,2] 贪心刷题
  • 3、Kafka 核心架构拆解和总结
  • 私网IP地址范围解析与应用指南
  • 低代码 x AI,解锁数智化应用的创新引擎
  • 自组织映射SOM
  • C++_MD5算法
  • Diamond iO:实用 iO 的第一缕曙光
  • Meta正为AI眼镜开发人脸识别功能
  • 光大华夏:近代中国私立大学遥不可及的梦想
  • 公募基金解读“一揽子金融政策”:增量财政空间或打开,有助于维持A股活力
  • “半世纪来对无争议边界最深入袭击”:印巴冲突何以至此又如何收场?
  • 刘诚宇、杨皓宇进球背后,是申花本土球员带着外援踢的无奈
  • 巴基斯坦外交部:印度侵略行径侵犯巴主权