指针和引用的主要区别
- 定义:
- 指针:指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。它可以被重新赋值以指向不同的地址。例如:
int *ptr;
定义了一个指向 int
类型的指针 ptr
。
- 引用:引用是已存在变量的别名,一旦初始化后就不能再引用其他变量。例如:
int num = 10; int &ref = num;
这里 ref
就是 num
的引用。
- 内存分配:
- 指针:指针本身需要分配内存来存储所指向变量的地址。例如,在 32 位系统中,指针通常占用 4 个字节,64 位系统中占用 8 个字节。
- 引用:引用本身不占用额外的内存空间,它和被引用的变量共享同一块内存。
- 可空性:
- 指针:指针可以为空(
nullptr
),即不指向任何有效的内存地址。例如:int *ptr = nullptr;
- 引用:引用必须在定义时初始化,并且不能为
null
。试图定义一个空引用是不合法的,例如 int &ref;
是错误的,必须写成 int num = 10; int &ref = num;
- 重新赋值:
- 指针:指针可以在其生命周期内重新赋值,指向不同的变量。例如:
int num1 = 10, num2 = 20;
int *ptr = &num1;
ptr = &num2;
- 引用:引用一旦初始化,就不能再引用其他变量。例如:
int num1 = 10, num2 = 20;
int &ref = num1;
// 下面的代码是错误的,ref 不能再引用 num2
// ref = num2; 这里是赋值操作,不是改变引用对象
函数参数传递场景
- 指针作为参数:
- 不同效果:函数可以通过指针修改调用者传递的变量的值,因为指针传递的是变量的地址。指针作为参数可以为空,在函数内部需要检查指针是否为空以避免空指针引用错误。
- 适用场景:当需要修改传入的变量值,并且允许传入空指针表示特殊情况时适用。例如,动态内存分配函数
malloc
返回 void*
指针,调用者可以传递 nullptr
来表示不进行分配。
- 示例:
#include <iostream>
void incrementWithPointer(int *num) {
if (num) {
(*num)++;
}
}
int main() {
int num = 5;
incrementWithPointer(&num);
std::cout << "After increment: " << num << std::endl;
return 0;
}
- 引用作为参数:
- 不同效果:函数可以通过引用修改调用者传递的变量的值,引用传递是直接对原变量的别名操作,不需要像指针那样解引用。引用不能为空,所以函数内部不需要进行空值检查。
- 适用场景:当需要修改传入的变量值,并且不允许空值传入时适用。例如,
std::swap
函数通常使用引用参数,因为交换的对象必须是有效的对象。
- 示例:
#include <iostream>
void incrementWithReference(int &num) {
num++;
}
int main() {
int num = 5;
incrementWithReference(num);
std::cout << "After increment: " << num << std::endl;
return 0;
}
函数返回值场景
- 指针作为返回值:
- 不同效果:可以返回堆上分配的内存地址,调用者可以通过指针继续操作这块内存。但是调用者需要负责释放内存,否则会导致内存泄漏。返回的指针可以为空,表示特殊情况。
- 适用场景:当需要返回动态分配的内存或者表示可能不存在的对象时适用。例如,在实现一个查找函数,可能找不到对应元素返回
nullptr
。
- 示例:
#include <iostream>
int* allocateMemory() {
return new int(10);
}
int main() {
int *ptr = allocateMemory();
std::cout << "Value: " << *ptr << std::endl;
delete ptr;
return 0;
}
- 引用作为返回值:
- 不同效果:返回对已存在对象的引用,调用者可以直接操作原对象。但是不能返回局部变量的引用,因为局部变量在函数结束时会被销毁,导致引用悬空。
- 适用场景:当需要返回类内部的成员变量或者全局变量等生命周期长于函数调用的对象时适用。例如,
std::vector
的 front
函数返回对第一个元素的引用。
- 示例:
#include <iostream>
int globalNum = 10;
int& getGlobalRef() {
return globalNum;
}
int main() {
int &ref = getGlobalRef();
ref++;
std::cout << "Global num: " << globalNum << std::endl;
return 0;
}