C/C++指针基本概念说明:取址还是引用?别傻傻分不清楚!

0. 前言

📣按照国际惯例,首先声明:本文只是我自己学习的理解,虽然参考了他人的宝贵见解及成果,但是内容可能存在不准确的地方。如果发现文中错误,希望批评指正,共同进步。

本文将通过下面四个(两个符号用在两种场合)重要符号,详细解释 C/C++ 中的指针。

符号名称语言环境含义说明
*指针声明符C/C++声明一个指针变量
&取地址运算符C/C++获取变量的内存地址
*解引用运算符C/C++访问指针所指向的内容
&引用声明符C++给变量起别名

注意区分上面说的声明符和运算符:声明符用于变量声明中的类型描述,运算符用于表达式中的操作。

1. * —— 指针声明符(Pointer Declaration)

在 C/C++ 中,* 用于在开头声明一个指针变量。

示例(C)

int *p;  // p 是一个指向 int 类型的指针

示例(C++)

double *d;  // d 是一个指向 double 类型的指针

📝 注意:在 C 语言中,int* a, b; 表示只有 a 是指针,而 b 是普通整型。不要误以为是两个指针!

2. & —— 取地址运算符(Address-of Operator)

在 C/C++ 中,& 是用于获取变量的内存地址运算符

示例

int x = 10;
int *p = &x;  // 将 x 的地址赋值给指针 p

此时,p 存储的是 x 的地址,而不是 x 的值。

输出地址

	printf("x 的地址:%p\n",& x);
	printf("p 的值:%p\n", p);

输出相同,因为 p == &x

在这里插入图片描述

3. * —— 解引用运算符(Dereference Operator)

使用 * 运算符来访问指针所指向的内存中的值。

示例

int x = 20;
int *p = &x;

printf("*p = %d\n", *p);  // 输出 20
*p = 30;                   // 修改指针指向的内容
printf("x = %d\n", x);     // 输出 30
  • p 是变量x的地址
  • *p 是这个地址中保存的数据(即 x 的值)

4. & —— 引用声明符(Reference Declaration)【仅限 C++】

在 C++ 中,& 还可以用于定义“引用”,即变量的别名。

示例

#include<stdio.h>
#include <iostream>

int main()
{
	int x = 42;
	int& ref = x; //ref是x的引用(别名)

	ref = 99;
	std::cout << "x=" << x << std::endl;

	return 0;
}

引用的特点

  • 必须初始化。
  • 通常用于函数参数传递,避免拷贝。
  • 初始化后不能改变引用目标,不能重新绑定,请见下例:
#include<stdio.h>
#include <iostream>

int main()
{
	int x = 42;
	int y = 88;
	int& ref = x; //ref是x的引用(别名)
	int& ref = y; //重复引用

	return 0;
}

输出错误提示:

在这里插入图片描述

引用有什么用?

你可能会想问引用只是给变量取一个别名,这有什么意义呢?

其实学过Python的同学在此应该会有熟悉的感觉,这其实就是in-place操作。

  1. 性能优势

    • 避免拷贝:对于大型对象(如类实例、容器等),传递引用比传递对象本身要高效得多,因为不需要进行深拷贝。
    • 减少内存占用:由于引用只是变量的一个别名,不占用额外的内存空间。
  2. 简化语法

    • 相比于指针,引用提供了更自然的语法。你不需要担心空指针问题,并且引用总是有效的(除非引用本身未初始化)。
  3. 增强功能

    • 支持多态:当你传递基类类型的引用时,如果实际传递的是派生类的对象,则可以在函数内部利用多态性调用派生类的方法。
    • 实现链式编程风格:通过返回引用,可以让方法调用链接起来,形成流畅的API设计。

5. 对比总结表

运算符语言出现场景功能说明示例
*C/C++声明语句中声明一个指针类型int *p;
&C/C++表达式中获取变量的地址int *p = &x;
*C/C++表达式中解引用,访问指针指向的内容*p = 10;
&C++声明语句中声明引用(别名)int &ref = x;

✅ 总结口诀(帮助记忆)

  • * 在声明中 → 是指针
  • & 在表达式中 → 是取地址
  • * 在表达式中 → 是解引用
  • & 在 C++ 声明中 → 是引用

6. 复杂情况举例分析

示例 1:多级指针(二级指针)

#include<stdio.h>
#include <iostream>

int main()
{
	int x = 10;
	int* p = &x;  //p是一级指针
	int** pp = &p;  //pp是二级指针

	printf("p是%p\n", p);
	printf("pp是%p\n", pp);
	printf("pp进行1次解引用%p\n", *pp);  //1次解引用后仍是地址
	printf("pp进行2次解引用%d\n", **pp);  //2次解引用后是int型数值
}
  • *pp 得到的是 p
  • **pp 得到的是 *p,也就是 x

输出为:

在这里插入图片描述

示例 2:函数中使用引用(C++)

#include<stdio.h>
#include <iostream>

void add1(int& num)  //函数无返回值,直接修改外部变量
{
	num++;  
}

int main()
{
	int x = 0;
	add1(x);
	std::cout << x << std::endl;
}

参数 num 是原始变量的别名(直接操作内存中的值),add1函数没有返回值,而是通过引用实现对实参的修改。

输出为:

在这里插入图片描述

示例 3:函数中使用指针(C)

#include<stdio.h>
#include <iostream>

void add1(int* num)  //函数无返回值,直接修改外部变量
{
	(*num)++;  
}

int main()
{
	int x = 0;
	add1(&x);
	std::cout << x << std::endl;
}

效果与上例相同,但语法不同。

7. 指针使用常见错误

错误类型错误示例问题分析修正方法
野指针int *p; *p = 10;指针 p 未初始化,指向随机内存地址,解引用会导致未定义行为(崩溃或数据损坏)。初始化指针:int x; int *p = &x;
悬垂指针int *p = &x; free(&x); *p = 5;指针 p 指向的内存已被释放(如变量 x 离开作用域),但 p 仍被使用,导致非法访问。释放后置空:p = NULL;(或确保作用域有效)
类型不匹配的指针double d = 3.14; int *p = &d;double* 强制赋给 int*,解引用时因类型解释错误导致数据读取错误。确保指针类型与目标类型一致。
指针越界访问int arr[3] = {1,2,3}; int *p = arr; *(p+3) = 4;指针算术越界(访问 arr[3]),破坏相邻内存或触发段错误。严格限制指针移动范围:0 ≤ i < 数组长度
返回局部变量指针int* func() { int x=10; return &x; }返回局部变量 x 的地址,但 x 在函数退出后销毁,返回的指针无效。返回动态内存或静态变量地址:static int x;
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

使者大牙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值