目录
指针和引用的区别是什么?
1. 定义和语法
- 指针:指针是一个变量,存储另一个变量的地址,可以指向任意内存位置。
int a = 10;
int* ptr = &a; // ptr 是指向 a 的指针
- 引用:引用是某个变量的别名,在定义后必须初始化,且一旦绑定到某个对象,就不能改变。
int a = 10;
int& ref = a; // ref 是 a 的引用
2. 内存地址
- 指针:指针是一个变量,它存储一个内存地址,使用时可以修改指向的对象。
int a = 10;
int* ptr = &a;
ptr = nullptr; // 可以指向 nullptr 或其他对象
- 引用:引用本质上是某个对象的别名,它直接绑定到对象,不能修改引用所指向的对象。
int a = 10;
int& ref = a;
// ref 不能重新绑定到其他对象
3. 空指针/空引用
- 指针:指针可以为空,即可以指向
nullptr
(C++11 引入)或NULL
。
int* ptr = nullptr; // 指针可以为空
- 引用:引用不能为空,必须绑定到一个有效的对象。没有“空引用”的概念,必须确保引用始终有效。
4. 重新绑定
- 指针:指针可以在程序运行期间改变指向的对象。
int a = 10, b = 20;
int* ptr = &a;
ptr = &b; // 可以重新指向 b
- 引用:引用在初始化后不能更改绑定的对象。
int a = 10, b = 20;
int& ref = a;
// ref = &b; // 错误,引用不能重新绑定
5. 运算符
- 指针:指针有专门的运算符
*
(解引用) 和&
(取地址)。你需要通过*
来访问指针指向的值。
int a = 10;
int* ptr = &a;
*ptr = 20; // 修改 a 的值为 20
- 引用:引用本质上是变量的别名,不需要使用运算符就可以直接访问对象。
int a = 10;
int& ref = a;
ref = 20; // 修改 a 的值为 20
6. 数组与动态内存
- 指针:指针可以指向动态分配的内存,也可以遍历数组或指向对象数组的某个元素。
int* arr = new int[5]; // 指针可以指向动态分配的内存
arr[0] = 1;
delete[] arr; // 手动释放动态分配的内存
- 引用:引用不能用于指向动态分配的内存或遍历数组。引用通常只适用于栈上的对象。
7. sizeof
- 指针:
sizeof
指针返回的是指针自身的大小(一般是 4 或 8 个字节,取决于系统架构),而不是指针指向的对象的大小。
int* ptr;
std::cout << sizeof(ptr); // 输出指针的大小
- 引用:引用本质上是对象的别名,
sizeof
引用返回的是引用所指对象的大小。
int a = 10;
int& ref = a;
std::cout << sizeof(ref); // 输出 a 的大小
何时使用指针,何时使用引用?
1. 使用引用的场景
- 简单的别名:当你不需要更改对象的引用关系,仅仅是为了给某个对象一个别名时,使用引用。
int a = 10;
int& ref = a; // ref 是 a 的别名
- 参数传递:当函数需要修改参数值或避免不必要的拷贝时,使用引用传递参数。
void modify(int& x) {
x = 20;
}
int a = 10;
modify(a); // a 被修改为 20
- 常量引用:当你需要传递一个大对象但不希望修改它时,使用常量引用来避免拷贝。
void print(const std::string& str) {
std::cout << str;
}
2. 使用指针的场景
- 动态内存管理:当你需要在堆上分配和管理内存时,使用指针。
int* ptr = new int(10);
delete ptr; // 需要手动释放内存
- 可能为空的对象:如果对象可能为空或者不存在时,使用指针可以表示“无对象”状态(通过
nullptr
或NULL
)。
int* ptr = nullptr; // 指针可以为空
- 指向不同对象:如果你需要让变量在程序运行中指向不同的对象或资源时,使用指针。
int a = 10, b = 20;
int* ptr = &a;
ptr = &b; // 可以让指针指向不同对象
- 数组或集合遍历:指针经常用于遍历数组或集合,尤其是在低级别操作或与 C 接口交互时。
int arr[5] = {1, 2, 3, 4, 5};
int* ptr = arr;
for (int i = 0; i < 5; ++i) {
std::cout << *(ptr + i);
}
总结
- 引用:适用于不需要改变引用对象、参数传递、常量传递等场景,简洁、安全且高效。
- 指针:适用于动态内存管理、可以为空的对象、多态(如通过基类指针操作子类对象)、指向不同对象或数组遍历等场景。指针灵活但需要小心内存管理和空指针问题。