C初阶--指针初阶(上):什么是指针+指针类型+野指针

目录

☀️1. 指针是什么?

 2. 指针和指针类型🏠 

1.指针类型决定了,在解引用指针的时候能访问几个字节(有多大的权限)

2.指针类型决定了,指针进行+1、-1的时候,一步走多远(步长)

3.指针类型的意义用处在哪呢?

1.修改成字符指针存放整型数组的地址

❗❗❗2.关于整型指针和字符指针赋值后执行结果一样的本质原因❗❗❗

 区别:特别注重!!!!!!✔️✔️✔️

3.野指针

3.1 野指针成因

1. 指针未初始化

2. 指针越界访问

3. 指针指向的空间释放

3.2 如何规避野指针


☀️1. 指针是什么?

以32位机器为例,机器里面是有一根根物理的电线的,其中就包括地址线。我们假设高电平为1,低电平为0。那么32位机器中32根地址线一旦通电,电信号转换成数字信号,就可以产生2^32个这样的二进制序列,就可以作为2^32个地址,就可以管理2^32个内存单元,也就是2^32个字节的内存空间

在广阔的内存空间上,存在着很多变量,这就需要用某种方式来表示各个变量在内存中的"位置",这就是地址

指针理解的2个要点:

1. 指针是内存中一个最小单元的编号,也就是地址

2. 平时口语中说的指针,通常指的是指针变量👇,是用来存放内存地址的变量

指针变量

我们可以通过&(取地址操作符)取出变量的内存起始地址,把地址可以存放到一个变量中,这个变量就是指针变量

单目操作符&❄️

将&运算符写在变量名之前,就可以得到该变量的地址--> &a  取出a的地址(生成指向a的指针)

int main()
{
	int a = 10;//a是整形,占用4个字节的内存空间,每个字节都有对应的地址
	int* pa = &a;
//&a - 得到的是a的地址(指针),其实得到的是a所占内存中4个字节中第一个字节的地址,pa是指针变量

	pa = 10;

	return 0;
}
🌀 总结:
指针变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。
那这里的问题是:
一个小的单元到底是多大?(1个字节)
如何编址?
经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。
对于 32 位的机器,假设有 32 根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是(1或者0 );
那么 32 根地址线产生的地址就会是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
...
11111111 11111111 11111111 11111111
这里就有 2 32 次方个地址。
每个地址标识一个字节,那我们就可以给 2^32Byte == 2^32/1024KB ==
2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB 4G 的空间进行编址。
这里我们就明白:
  • 32位的机器上,地址是320或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
  • 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
总结:
  1. 指针变量是用来存放地址的,地址是唯一标示一个内存单元的。
  2. 指针的大小在32位平台是4个字节,在64位平台是8个字节

 2. 指针和指针类型🏠 

int num = 10;
p = #
要将 &num num 的地址)保存到 p 中,我们知道 p 就是一个指针变量,那它的类型是怎样的呢?
我们给指针变量相应的类型。
char   * pc = NULL ;
int   * pi = NULL ;
short * ps = NULL ;
long   * pl = NULL ;
float * pf = NULL ;
double * pd = NULL ;

🏢指针的定义方式是: type + *

总结:

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

指针类型的意义是什么?🚀

1.指针类型决定了,在解引用指针的时候能访问几个字节(有多大的权限)

  • int*类型指针:把一个整型变量a的地址放在一个整型指针里
int main()
{
	int a = 0x11223344;
	int* pa = &a;
	*pa = 0;

    return 0;
}

操作步骤:1.先按f10开始调试,之后像下面这样打开内存监视窗口

 2.接着按下图这样继续走,发现a开始存放的是随机值,接着内存就被改成44 33 22 11的数据,至于与输入时为什么是倒着存放的不必解读,不影响接下来的步骤

 3.可以看到在经过*pa=0这一步时,也就是对a进行解引用操作,发现变量a里面存放的数据变成了00 00 00 00,

4.接下来做一些小改动,把&a放在一个字符指针里面去,重复1,2,3的步骤,可以发现只改a所占4个字节里面的第一个字节,改为char* pc=&a;

int main()
{
	int a = 0x11223344;
	
	char* pc = &a; 
	*pc = 0;

	return 0;
}

  然而两边的代码差异就是指针类型,所以就是指针类型导致的结果

 🚌总结:

指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节),个人的理解就是站在指针的角度考虑问题,把地址给字符指针的时候它认为它指向的就是一个字符的数据
我们可以在适当的情况下使用适当的指针类型
比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

📍一些需要注意的点:

为什么char*pc可以存放int a的地址?

答:pc是有能力把a的地址存起来的,因为pc大小是4byte,也是个指针变量,就可以存放地址

🚩警告:

以下是会出现的警告,因为&a的类型是int*,而左边是int*

2.指针类型决定了,指针进行+1、-1的时候,一步走多远(步长)

int main()
{
	int a = 10;
	int*  pa = &a;
	char* pc = &a;
	printf("%p\n", pa);
	printf("%p\n", pa+1);

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

	return 0;
}

代码经过执行后可以看到差距: 

可以从以上代码得到总结: 

对于int*pa,它认为它指向的是整型,所以pa+n跳过n个整型

对于char*pc,它认为它指向的是字符,所以pc+n跳过n个字符

总结:指针的类型决定了指针向前或者向后走一步有多大(距离) 

3.指针类型的意义用处在哪呢?

以下的1和2的知识点是连贯的☑️

以往的知识我们可以知道,访问数组元素可以用数组名+下标例如:arr[1]来访问数组元素

也可以用*(p+i)的形式给数组元素赋值👇👇

int main()
{
	int arr[10] = {0};
	int* p = &arr[0];

	int i = 0;
	for (i = 0; i < 10; i++)
	{
	    *p = i + 1;
		p++;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

从内存中看:可以发现数组一个元素就是一个int类型,一个元素占用4个字节的空间,那么10个就占用40个字节的空间

图解:

 

1.修改成字符指针存放整型数组的地址

 那么我们修改之后,改成以下这个样子,结果又会如何呢

int main()
{
	int arr[10] = {0};
	char* p = &arr[0];

	int i = 0;
	for (i = 0; i < 10; i++)
	{
	    *p = i + 1;
		p++;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

 图解:

❗❗❗2.关于整型指针和字符指针赋值后执行结果一样的本质原因❗❗❗

注意区别:

int main()
{
    int arr[10] = {0};
    char* p = &arr[0];

    int i = 0;
    for (i = 0; i < 10; i++)
    {
        *p = i + 1;
        p=p+4;//这个位置修改了
    }
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }

    return 0;
}

 就会发现,得到神似int* p的结果

还有,内存分布与写成int*p的指针也是一样的,它们没有区别吗?不,区别大了

 

 区别:特别注重!!!!!!✔️✔️✔️

1.int *p=&arr[0] ,p++

这里是把绿色数字包括那部分全部占满了 

2.char* p=&arr[0],p=p+4

这里仅仅是把10个整型中每一个整型的第一个字节的数据改了,也就是上图中的黑色数字1234,并没有把每一个整型所占的4个字节全改完,还是有着本质区别的

3.野指针

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

3.1 野指针成因

1. 指针未初始化

#include <stdio.h>
int main()
{ 
 int *p;//局部变量指针未初始化,默认为随机值
    *p = 20;
 return 0;
}

这里并没有有效的指向,按我的理解是正常逻辑是先&,生成指向该地址的指针,这才符合正常操作,而不是创建一个不知道明确指向的指针

2. 指针越界访问

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };

	int* p = arr;
	int i = 0;
	for (i = 0; i <= 11; i++)
	{
		//当指针指向的范围超出数组arr的范围时,p就是野指针
		*(p++) = i;
	}
	return 0;
}

类比:就像有一条狗,它是有主人的,但有一天它走丢了,就是指向最后一个位置的时候,那它就是走丢了,它是一条野狗,变得非常危险

3. 指针指向的空间释放

int* test()
{
	int a = 10;
	return &a;
}
int main()
{
	int*p = test();
	*p = 100;
	return 0;
}

 补充:局部变量a出了它所在范围就销毁了

3.2 如何规避野指针

1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放,及时置 NULL
4. 避免返回局部变量的地址(上一题,局部变量a出了它所在范围就销毁了)
5. 指针使用之前检查有效性(记得对指针初始化,有明确指向的时候给它初始化,没有明确指向的时候,给它初始化为NULL指针)
案例:正确使用
int main()
{
	int a = 10;
	int* p = &a;

	//一个指针不知道应该指向哪里的时候,暂时可以初始化为NULL;
	//int* p = NULL;
	if (p != NULL)
	{
		*p = 100;
	}

	return 0;
}

本章未完,还有下半篇....欢迎大佬补充!

评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Dream_Chaser~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值