初阶C语言 第五章-------《指针》 (野指针,二级指针,数组指针)知识点+基本练习题+深入细节+通俗易懂+完整思维导图+建议收藏

绪论

        书接上回,经过操作符章你应该对编程中的运算有了一个更深入的了解,了解到了他底层的运算(二进制运算),以及各种操作符的具体用法。而这章的指针是我们在C语言学习中很重要的一个部分此处为初阶指针后期会更新进阶般,希望你可以通过我这篇文章,对指针有个更好的认识。

 18245ee000ef4f1bae3d771a162ce48c.webp

 

所以安全带系好,发车啦(建议电脑观看)。

        附:红色,部分为重点部分;蓝颜色为需要记忆的部分(不是死记硬背哈,多敲);黑色加粗或者其余颜色为次重点;黑色为描述需要

思维导图:

dccf396beaa54faa9780d21c47960e48.png

 

要XMind思维导图的话可以私信哈

目录

绪论

1.指针的定义

1.1指针是什么

1.1.1指针:

1.1.2内存:

1.2指针变量(地址的变量)

1.2.1指针变量:

地址编号的产生:

2.指针和指针类型

2.1指针类型

2.2指针的解应用

3.野指针

3.1野指针的来源

3.1.1野指针:

3.1.2野指针的危害:

3.1.3常见野指针的成因:

3.2如何避免野指针

4.指针的运算

4.1指针 + - 整数

4.2指针-指针

4.3指针的关系运算

5.指针和数组

6.二级指针及多级指针

7.指针数组

 


 

1.指针的定义

1.1指针是什么

知识点:

1.1.1指针:

内存最小单元的编号,即地址(指针和指针变量不一样,我们常说的指针一般指指针变量)

细节(注意点):

1.1.2内存:

我们在使用计算机的过程中每使用一个程序都会占用我们的内存空间,而指针就是某个内存的地址编号,如今内存通常在计算机中以4G、16G的形式存在,当我们打开我们的任务管理器就能看到自己当前内存等的使用情况。

a2eb8b626ffc4451affb783b39562a02.png

 

例:整形变量a在内存中占用的内存使用情况如下

1830da68f59e4fbbbb96cb2a4e495707.png

 附:&a取的是首元素地址(低地址),底下是低地址,高处是高地址

通过上面所画的图,在每个内存单元的大小时一个字节,其对应的地址编号占四个字节(在32位机器上时)

 

1.2指针变量(地址的变量)

知识点:

1.2.1指针变量(一种存放指针(地址)的变量):

        当我们先要去使用指针时,我们就要创建一个指针变量如:int * p = &a(此处p变量存放的是a的地址);同过此处指针变量可以去访问a变量的地址。此处的p就是指针变量。因为p指针已经找到了a的内存所在的地址,指针变量就可以通过解应用操作符,去改变a变量。(就好比你已经知道了一个地址,你就可以通过这个地址找到所对应的地方)

 #define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int a = 0;
	int* p = &a;
	printf("%p\n", p);//p指针指向a变量的地址
	printf("%p\n", &a);
	printf("%d\n", *p);//通过*解引用找到a,并且可以改变a
	printf("%d\n", a);
	*p = 20;
	printf("%d\n", a);

	return 0;
}

45ce0753e01e4c58b922f97d406df82e.png

细节:

通过上面的知识点可以概括成,指针变量就是用来存放某个地址的变量。而对于地址来说,每个地址都有对应编号,而这个编号又是怎么产生的呢?

地址编号的产生:

        对于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的空闲进行编址。 同样的方法,那64位机器,如果给64根地址线,那能编址 2 ^ 64 byte == .... = 16G

        在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以 一个指针变量的大小就应该是4个字节。(32位平台上指针变量大小4byte)

        那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。(64位平台上指针变量大小是8byte),所以指针大小要看在什么机器上

746a751197334965a83cd86f02215b07.png   62749f996b0a45a2bfa8fdffcea03805.png
 x84:32位机器   ; X64 :64位机器

2.指针和指针类型

2.1指针类型

知识点:

同变量一样,指针也有不同的指针类型int *、 char * 、 long *、.........

        但我们通过上面的指针大小又发现他们不同类型间的大小和正常的变量类型大小不一样是相同的,这样不同的类型岂不是没什么区别?

        其实,指针变量类型的大小虽然确实是相同的,但是他们的意义不同,一般来说,int * 的指针变量,存放的地址是int类型的变量地址。一般来说,char * 的指针变量,存放的地址是char类型的变量地址, ..... 

        不同的指针类型将会决定其指针在前进or后退时的前进or后退的内存单元大小,如int * 类型的指针,当他往前走时是每次走4个byte,同理char * 类型 每次往前走1个byte

也就是步长

char * : 

2a00fdf15b8848f3af41af7d89973633.png2b974a6e3a344568890671b0eca85ce9.png

 int * :

3d923315e91b484eade99a4361a377ba.png

 .....

2.2指针的解应用

知识点:

指针通过 * 解应用操作符来找到所指定的地址空间,并且可以通过这个地址去改变该空间内的值

注:指针的类型决定了指针解应用的权限,即一次性改变的值(通过上面所截的图就是此处)

int main()
{
	int a = 0x11223344;
	char* p = (char*)&a;
	for(int i = 0; i < 4 ; i++)
	*p++ = 0;//同过后++让指针往前走
	return 0;
}

 

3.野指针

3.1野指针的来源

知识点:

3.1.1野指针:

是一种我们在日常写代码时容易犯的错,就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。

细节:

3.1.2野指针的危害:

如果指针随意的去指向一个你自己都不知道的地方,假如你将这个指针改变了也就是将一个你都不知道的位置进行改变,假如在一个大型的程序中这样可能会将你自己所设的值改变,而自己却很难发现原因。

3.1.3常见野指针的成因:

  1. 指针未初始化                                                                                                                    从函数栈帧的创建和销毁你可以知道局部的变量在为初始化前都是一个随机值,同理指针变量也是如此,当你没有去初始化时,他所指向的空间地址也是一个随机的。13b447c957474999be29b6fade023eac.png​​​​​​​​此处指针未初始化,就会让其指向一个未知的地址空间
  2. 指针越界访问                                                                                                                    同数组越界类似,当一个指针去访问一个数组时可能会在向前走时,走过了数组的范围指向了数组外的未知地址空间。​​​​​​​1ee97a0cf019409caf132f06a9d5ec3f.png​    此处指针在不断往后走时因为i<=12所以将会使指针指到数组外的未知地址
  3. 指针指向的空间已经释放                                                                                                  当指针指向函数内的变量时,其函数用完后就会把空间还给内存,即:假如你在主函数内去使用函数内所指定的变量就会导致指向的空间地址已经不是该变量的地址

         1a26a1a3d07f4693bde7c4d14bf1266b.png​​​​​​​        此处可以看到当打印*p指向的空间时,虽然第一次打印出正确的10 但是当第二次空间彻底释放后将会打印出随机值。

 

 

3.2如何避免野指针

知识点:

1. 指针初始化

2. 小心指针越界

3. 指针指向空间释放即时将指针置为NULL、避免返回局部变量的地址

4. 指针使用之前检查有效性

    如:指针不能将NULL改变,NULL的地址不能进行访问的这是系统的一个区域我们用户无法进行改变,我们正常写代码的区域是用户区域;就好比我们的操作系统的内存的内核区,他是一个专门给系统使用的。

所以我们在用指针改变某值时要确定这个值的地址不是NULL

127518fbc2cd42b5a9d2165f644ace9c.png       if ( p !=  NULL)

 

4.指针的运算

4.1指针 + - 整数

知识点:

指针的加减整数其可以大概的看成指针的前进后退。

f815379a6fce4716b8778b8c229d91c8.png

 

4.2指针-指针

知识点:

在这两个指针指向同一块空间(一般是数组)的前提下,当这两个指针向减后得到的绝对值是两指针之间的元素个数的(绝对值是因为当有小的指针减掉大的指针)

40d3aebd4ebc4e97bef2a343c4519af4.png

4.3指针的关系运算

知识点:

        允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

157d96fc0df54929854627ed3bbb83bf.pngcb262fe6fdc8475998c1e7cb19997c95.png

当p处所指的内容为一个随机值时就可以表示其已经越界了但图1时符合标准规定的,而图二并不符合标准。

5.指针和数组的关系

知识点:

  • 数组名为首元素的地址(两种情况除外,数组章写过)
  • 在创建指针变量时,可以直接初始化成数组名(不用加&取地址操作符)

细节:

arr[i] 其实就可以看成 *(arr+i) 。因为arr为首元素的地址,而指针指向的变量也是指向其首元素地址,所以当指针指向数组时可以表示成,arr[i]  == *(p+i) == *(arr+i),指针通过前进指向下一个空间(注意类型的不同情况)。8ce3e5a4b0724646bb60a3085447cb5c.png

 附:加法具有交换律,所以还可以写成*(i+p) / *(i+arr)== i [arr]  , [ ] 是操作符而i与arr只是操作数所以可以交换。

所以p+i其实就是arr【i】的地址,再加上*找到其空间(所以在改变其数组内容时可以直接通过指针进行改变,注指针甚至可以在其他函数内对主函数的数组进行改变)

6.二级指针及多级指针

知识点:

指针变量也是变量,所以他就会有相对应的地址。即我们在二级指针中所存的地址其实就是(一级)指针的地址。584a935216554182b1acb120ab1f8a92.png

附: 多级指针的用法类似如三级指针,其用法就是***p2 就可以找到a

7.指针数组

知识点:

虽然叫着指针,指针数组任然是一个数组。他就是数组的一种类型如int arr[ ] ,  字符数组一样。其表现形式是int* arr[ ]    ....   ;  在整形指针中他存放的是一个个整形,而在指针数组中的整形指针数组他存放的是一个个整形指针

df22df1ee36d48f09dffbc4a354f6cff.pngb1300fd807ad480f938bb255b04f44d5.png

 

 所以当我们用arr[i]找到其存放的内容,在加上*  -->  *(arr[0]) ==  *(&a)  == a

 

 

本章完。预知后事如何,暂听下回分说。

 

持续更新大量C语言细致内容,三连关注哈

 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
根据提供的引用内容,我们可以看出这是一个关于堆的实现和应用的问题。引用\[1\]提到了一种使用顺序表存储的方式来实现堆,但是这种方式存在空间浪费的问题。引用\[2\]列举了堆的接口函数和堆排序的过程。引用\[3\]介绍了一种常用且优化的表示方法,即左孩子右兄弟表示法。 根据问题描述,警告C6386是指在写入"popk"时发生了缓冲区溢出。根据提供的代码,问题出现在源文件的第64行。具体原因可能是在该行代码中,将数据写入了名为"popk"的缓冲区,但是该缓冲区的大小不足以容纳写入的数据,导致溢出。 为了解决这个问题,我们需要检查源文件中的相关代码,确保在写入缓冲区时不会超出其大小限制。可能需要调整缓冲区的大小或者使用更安全的写入方式来避免缓冲区溢出的问题。 #### 引用[.reference_title] - *1* *3* [二叉树第一弹之树和堆的概念和结构、基础堆接口函数的实现(编写思路加逻辑分析加代码实操,一应俱全的汇总...](https://blog.csdn.net/AMor_05/article/details/127175020)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [第九章:C语言数据结构与算法初阶之堆](https://blog.csdn.net/yanyongfu523/article/details/129582526)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

溟洵

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

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

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

打赏作者

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

抵扣说明:

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

余额充值