C语言 | 指针篇(1)

文章详细介绍了指针的基本概念,包括指针作为内存地址、不同类型的指针及其用途,重点讨论了指针解引用和指针加减运算的原理。同时,文章强调了野指针的问题,列举了未初始化、越界访问和释放后仍使用的指针可能导致的问题,并给出了避免野指针的建议。
摘要由CSDN通过智能技术生成

目录

前言:为什么需要指针

1. 什么是指针

1.1 指针的理解​

2. 地址是怎么产生的

3. 为什么需要不同类型的指针

3.1 指针的解引用

3.2 指针+-整数

4. 野指针

4.1 导致野指针的原因

4.1.1指针未初始化

4.1.2 指针越界访问

4.1.3 指针指向的空间释放了

4.2 怎么避免野指针


前言:为什么需要指针

我们用计算机输入了一个数据,比如 a ,a 就被存储到内存中,我们下一次要使用 a 的时候,在 众多内存中找一个数据是十分困难的,于是我们引入指针,记录下 a 在内存中的地址,便于 a 的查找。就像我们平时生活中的编号,一座图书馆里如果没有编号,那我们找一本书需要挨个书架挨个书架找,这样效率很低,有了编号,比如图书馆北区3楼2号5层书架,我们可以快速地找到这本书。

1. 什么是指针

指针是内存中一个最小单元的编号,也就是地址。我们平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。

1.1 指针的理解

2. 地址是怎么产生的

对于32位机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电压(或者高电平)(用 1 表示)和低电压(低电平)(用 0 表示),把电信号转为数字信号,那么32根地址线产生的地址就会是

00000000 00000000 00000000 00000000    // 一个地址

00000000 00000000 00000000 00000001    //一个地址

......

11111111 11111111 11111111 11111111           //一个地址

这里就有 2 的 32 次方个地址。

每个地址标识一个字节,那我们就可以给 2^32Byte = 2^22KB = 2^12MB = 2^2GB = 4GB 的空间进行编址。同样的方法,64位机器,如果给64根地址线,就可以给 8 GB的空间进行编址。

地址也是需要存放在内存中的。在32位机器中,地址是32个0或者1组成二进制序列(也就是32个字节,一个字节存放一个二进制位),那么地址就得用4个字节的空间来存储,所以一个指针变量的大小是4个字节,只要是指针,在32位机器中它的大小就是4个字节。同样道理,64位机器上如果有64根地址线,那一个指针变量的大小是8个字节,8个字节才能存放一个地址。

32位机器下:

 

64位机器下: 

3. 为什么需要不同类型的指针
int* pb = NULL;
char* pc = NULL;
float* pd = NULL;

虽然指针的大小(32位机器)都是4个字节,但是我们仍然需要不同类型的指针,上面的代码表示

int * 类型的指针是为了存放 int 类型变量的地址

char * 类型的指针是为了存放 char 类型变量的地址

float * 类型的指针是为了存放 float 类型变量的地址

除了用于表示指针存放的数据类型之外,指针类型还有别的意义。

3.1 指针的解引用

首先我们先来感受同一个数据在不同指针类型的不同输出结果:

#include<stdio.h>

int main()
{
	int a = 0x11223344;//0x 表示十六进制
	int* pa = &a;
	char* pb = &a;

	printf("%x\n", *pa);//%x 表示输出十六进制的整型
	printf("%x", *pb);
	return 0;
}

 可以看出不同指针类型的输出结果是不一样的。

开始调试后,在内存中输入 &a,可以看到是小端存储(即低位放在低地址)。由于在十六进制和二进制的转换中,4个二进制位可以转换为1个十六进制位,而1个字节相当于8个比特位(即8个二进制位),所以1个字节可以存储2个十六进制位,而1个地址表示1个字节,所以44,33,22,11各放在一个地址中,即44 放在0x009BFAA0,33放在0x009BFAA1,22放在0x009BFAA2,11放在0x009BFAA3。pa 是 int* 类型,存储的数据大小是4个字节,存储了 44 33 22 11;pb 是 char* 类型,存储的数据是1个字节,所以只存储了44,所以解引用后打印结果不同。

总结:指针类型决定了指针解引用操作时访问多少个字节(指针的权限),解引用的时候访问的对象的大小为 sizeof (type)。

3.2 指针+-整数

理解完上面的内容之后,下面讲解指针+-整数,依旧用代码来感受不同:

#include<stdio.h>
int main()
{

	int a = 0x11223344;//0x 表示十六进制
	int* pa = &a;
	char* pb = &a;

	printf("%p\n", pa);
	printf("%p\n", pa + 1);

	printf("%p\n", pb);
	printf("%p\n", pb + 1);
	return 0;
}

 

 可以看出进行 +1 操作后,输出结果不一样,int 类型的指针 +1 跳过4个字节,char 类型指针 +1 跳过1个字节

总结:指针的类型决定指针 +-操作时的步长(步长就是跳过了多少字节),用

n * sizeof (type) 来计算步长。

4. 野指针

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

4.1 导致野指针的原因
4.1.1指针未初始化
int main()
{
	int* p; //指针未初始化,默认为随机值
	*p = 20;
	return 0;
}

没有初始化的指针 p 是野指针,这时编译器会报错。

4.1.2 指针越界访问
#include<stdio.h>
int main()
{

	int arr[10] = { 0 };
	int* p = arr;//数组名就是数组首元素的地址
	int i = 0;
	for (i = 0; i <= 11; i++)
	{
		*(p++) = i;
	}
	return 0;
}

 由于我们定义数组元素的个数为10,而循环的条件是 i <=11,而 i = 10 时,已经超出了数组的范围,属于越界访问,超出范围的数组元素的指针就是野指针。

4.1.3 指针指向的空间释放了
#include<stdio.h>
int* test6()
{
	int a = 10;
	return &a;
}
int main()
{
	int* p = test6();
	printf("%d\n", *p);

	return 0;
}

a的空间出了函数 test6 之后就销毁了,但仍把a的地址给了p,main 函数中,想通过地址 p 找到a时,根本找不到,p此时是野指针。

打个比方:张三在一家酒店住了一晚上,并告诉了李四自己在这家酒店住和自己的房间号,第二天张三退房了,但李四依旧根据这个房间号去找张三,张三此时已经不在这间房间了(也就是李四手里的这个地址已经没用了),李四想进入房间找人的行为是非法的。

可能可以打印出来但是这种访问是非法的

 虽然可以打印出 a 的值,但次数有限,后续打印的结果是随机值。

4.2 怎么避免野指针
  • 指针初始化,代码如下:
#include<stdio.h>
int main()
{
	int* p = NULL;
	int a = 10;
	p = &a;
	
	return 0;
}
  • 小心指针越界
  • 指针指向的空间释放,及时置NULL
  • 避免返回局部变量的地址
  • 指针使用之前检查有效性

指针的学习未完待续,欲知后事如何,请听下回分解!感谢阅读,请留下一个赞,如果有任何错误,欢迎指正!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值