C++ 引用、指针与赋值的深度剖析
一、引用的概念
引用是 C++ 中的一个重要特性,它可以看作是变量的别名。引用在创建时必须被初始化,这是引用的一个重要规则。因为引用本身不占用额外的内存空间,它只是已存在变量的别名,所以需要在声明时就指定它所引用的变量。例如:
int num = 10;
int& ref = num; // ref 是 num 的引用,它们指向相同的内存位置
std::cout << "num 的值:" << num << std::endl;
std::cout << "ref 的值:" << ref << std::endl;
ref = 20; // 通过引用修改所绑定变量的值
std::cout << "修改后 num 的值:" << num << std::endl;
std::cout << "修改后 ref 的值:" << ref << std::endl;
在上述代码中,ref
被声明为对 num
的引用,对 ref
的任何操作都等同于对 num
的操作,因为它们本质上是同一个内存位置的不同名称。
二、引用与指针的区别
- 内存占用与地址表示:
- 引用:引用本身不占用额外的内存空间,它只是已存在变量的别名,与被引用变量共享内存地址。例如上面代码中
ref
的内存地址和num
是完全相同的,在整个生命周期内,引用一旦初始化,就不能再绑定到其他变量上,始终指向最初绑定的那个变量。 - 指针:指针是一个独立的变量,它占用内存空间(通常在 32 位系统上为 4 个字节,64 位系统上为 8 个字节),用于存储另一个变量的内存地址。例如:
- 引用:引用本身不占用额外的内存空间,它只是已存在变量的别名,与被引用变量共享内存地址。例如上面代码中
int num = 10;
int* ptr = # // ptr 是一个指针,指向 num 的内存地址
std::cout << "num 的地址:" << &num << std::endl;
std::cout << "ptr 的值(即 num 的地址):" << ptr << std::endl;
指针可以在程序运行过程中重新指向其他变量的内存地址,具有更大的灵活性,但同时也需要更多的内存来存储指针本身的地址信息。
2. 操作符与语法:
- 引用:在声明和使用引用时,使用 &
符号,但这个 &
与取地址操作符的含义不同,它是引用声明的一部分。例如 int& ref = num;
是声明一个引用,而在其他地方使用 ref
时,就像直接使用原变量 num
一样,不需要额外的操作符。
- 指针:使用 *
操作符来访问指针所指向的变量的值,使用 &
操作符来获取变量的地址赋给指针。例如:
int num = 10;
int* ptr = #
std::cout << "通过指针访问 num 的值:" << *ptr << std::endl; // 使用 * 访问指针指向的值
*ptr = 20; // 通过指针修改所指向变量的值
std::cout << "修改后 num 的值:" << num << std::endl;
指针的操作相对引用来说更加复杂,需要小心使用 *
和 &
操作符,避免出现空指针解引用(即对一个未初始化或无效的指针进行 *
操作)等错误,这可能会导致程序崩溃。
三、引用与赋值的区别
- 数据复制与关联:
- 赋值:当进行赋值操作时,如
int a = 5; int b = a;
,会将变量a
的值复制一份赋给变量b
。此时a
和b
是两个独立的变量,它们在内存中有各自的存储空间,只是初始值相同。后续对a
的修改不会影响b
,反之亦然。例如:
- 赋值:当进行赋值操作时,如
int a = 5;
int b = a;
a = 10;
std::cout << "a 的值:" << a << std::endl; // 输出 10
std::cout << "b 的值:" << b << std::endl; // 输出 5
- 引用:如前所述,引用是变量的别名,与被引用变量共享内存,对引用的修改就是对原变量的修改,它们之间是一种紧密的关联关系,而不是简单的数据复制。
- 用途与场景:
- 赋值:常用于在程序中初始化新变量或者在不同变量之间传递数据副本,特别是当我们需要保留原始数据的独立性,不希望后续的修改相互影响时,就会使用赋值操作。
- 引用:通常用于函数参数传递和函数返回值,这样可以避免不必要的数据复制,提高程序的效率,同时也可以在函数内部直接修改外部变量的值,实现类似于“双向传递”的效果(但要注意避免滥用,以免使程序逻辑变得复杂难以理解)。
四、通过交换函数对比三者区别
以下是分别使用引用、指针和赋值来实现交换两个变量值的函数示例:
- 使用引用实现交换函数:
void swapByReference(int& num1, int& num2) {int temp = num1;num1 = num2;num2 = temp;
}int main() {int a = 5;int b = 10;std::cout << "交换前:a = " << a << ", b = " << b << std::endl;swapByReference(a, b);std::cout << "交换后:a = " << a << ", b = " << b << std::endl;return 0;
}
在这个例子中,swapByReference
函数使用引用作为参数,函数内部对参数的修改直接反映在调用函数的实参上,因为引用与实参共享内存,实现了变量值的交换。
2. 使用指针实现交换函数:
void swapByPointer(int* ptr1, int* ptr2) {int temp = *ptr1;*ptr1 = *ptr2;*ptr2 = temp;
}int main() {int a = 5;int b = 10;std::cout << "交换前:a = " << a << ", b = " << b << std::endl;swapByPointer(&a, &b);std::cout << "交换后:a = " << a << ", b = " << b << std::endl;return 0;
}
这里的 swapByPointer
函数使用指针作为参数,通过解引用指针来访问和修改所指向的变量的值,需要在调用函数时传递变量的地址,实现了与引用类似的交换效果,但语法上更加复杂,需要注意指针的有效性和正确使用,避免出现空指针等问题。
3. 使用赋值实现交换函数(这种方式不太符合常规的交换函数逻辑,但为了对比展示):
void swapByAssignment(int num1, int num2) {int temp = num1;num1 = num2;num2 = temp;std::cout << "函数内交换后:num1 = " << num1 << ", num2 = " << num2 << std::endl;
}int main() {int a = 5;int b = 10;std::cout << "交换前:a = " << a << ", b = " << b << std::endl;swapByAssignment(a, b);std::cout << "交换后:a = " << a << ", b = " << b << std::endl;
}
在这个 swapByAssignment
函数中,虽然在函数内部实现了两个参数值的交换,但由于函数参数是按值传递的,这种交换只是在函数内部的局部变量之间进行,对函数外部的实参 a
和 b
没有影响,这体现了赋值操作只是复制数据,而不会影响原始变量的特性。
五、引用的注意事项
- 引用必须初始化:引用在声明时必须被初始化,指向一个已经存在的变量,否则会导致编译错误。例如
int& ref;
是不合法的,必须写成int num = 5; int& ref = num;
这样的形式。 - 不能重新绑定:一旦引用被初始化绑定到一个变量,就不能再重新绑定到其他变量。例如:
int num1 = 5;
int num2 = 10;
int& ref = num1;
// ref = num2; // 这行代码不是重新绑定,而是通过引用修改 num1 的值为 num2 的值
试图将 ref
重新绑定到 num2
是不允许的,它始终与 num1
保持关联,除非 num1
的生命周期结束(例如 num1
所在的作用域结束),此时 ref
的引用也会失效。
看看其他人的
https://blog.csdn.net/weixin_56935264/article/details/124669712