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

c++中,"&"符号有时代表引用,有时代表取地址符。问你死未。

一、引用和取址

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

1)引用

cpp 复制代码
int a = 10;
int &ref = a;  // ref 是 a 的引用

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

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

cpp 复制代码
void func(int &x) {
    x = 20;  // 修改的是外部传入的变量
}

int main() {
    int a = 10;
    func(a);  // 这里传进去的是 a 的引用
    std::cout << a << std::endl;  // 输出 20
}

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

2)取地址符

cpp 复制代码
int a = 10;
int *p = &a;  // p 是指向 a 的指针

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

cpp 复制代码
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)引用

cpp 复制代码
void increment(int &x) {
    x++;
}

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

2)指针

cpp 复制代码
void increment(int *x) {
    (*x)++;
}

int a = 5;
increment(&a); // 需要传递 a 的地址

2、安全性

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

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

cpp 复制代码
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、 简化函数重载

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

cpp 复制代码
void foo(const std::string &s); // 接受左值
void foo(std::string &&s);      // 接受右值

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

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

另一个例子

cpp 复制代码
#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,这可以显著提高性能,因为不需要复制整个字符串的内容。

cpp 复制代码
void foo(const std::string &s) {
    // s[0] = 'A'; // ❌ 错误:不能修改 const 对象
    std::cout << s << std::endl; // ✅ 合法:只读访问
}
std::string str = "Hello";
foo(str); // 传递的是 str 的引用,而不是它的副本
相关推荐
TPBoreas2 小时前
Jenkins 改完端口号启动不起来了
java·开发语言
TE-茶叶蛋2 小时前
Vuerouter 的底层实现原理
开发语言·javascript·ecmascript
云闲不收3 小时前
设计模式原则
开发语言
秋名RG3 小时前
深入解析建造者模式(Builder Pattern)——以Java实现复杂对象构建的艺术
java·开发语言·建造者模式
技术求索者4 小时前
c++学习
开发语言·c++·学习
山猪打不过家猪4 小时前
(二)毛子整洁架构(CQRS/Dapper/领域事件处理器/垂直切片)
开发语言·.net
方博士AI机器人6 小时前
Python 3.x 内置装饰器 (4) - @dataclass
开发语言·python
weixin_376934636 小时前
JDK Version Manager (JVMS)
java·开发语言
Logintern097 小时前
【每天学习一点点】使用Python的pathlib模块分割文件路径
开发语言·python·学习
cykaw25907 小时前
QT 文件选择对话框 QFileDialog
开发语言·qt