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 的引用,而不是它的副本