登录  | 立即注册

游客您好!登录后享受更多精彩

扫一扫,访问微社区

QQ登录

只需一步,快速开始

开启左侧

[寒假笔记] [总结贴][C语言]如何把指针玩出花[Part.3]

[复制链接]
发表于 2023-1-2 17:41:14 | 显示全部楼层 |阅读模式
学习笔记
学习科目: C语言
学习安排: C语言是大一上学期的第一个目标
开始时间: 2022-09-11
结束时间: 2022-09-29
本帖最后由 王宇涵 于 2023-1-3 20:15 编辑
C 语言
Part.3  如何把指针玩出花

目录


Part.1  C 语言面向过程编程

Part.2  字符串与ASCII码

Part.3  如何把指针玩出花


/*本贴总结适用于已经学过 C 语言的同学,旨在提供一些技术应用时的小技巧*/

一、回顾一下指针


在 C 语言中,指针是一类特殊的变量

它们和其他变量不同,其他变量存储的是具体的值,而指针则是值的地址


我们在 C 语言里声明变量的过程,实际上是按照以下的流程来进行的:


第一步,C 语言为我们寻找一个存变量的内存块,把变量装进去;

第二步,C 语言会记录下当前内存块的地址,方便下次使用;

第三步,C 语言会把地址与变量名相关联。


这样我们就可以使用变量名直接修改变量的值,绕过地址这一步。

如果还是不明白,我举个例子,比如,我输入 int a = 0;


第一步,C 语言为我们寻找一个存变量的内存块,把 0 装进去;

第二步,C 语言会记录下当前内存块的地址,假如(0x867C897A),方便下次使用;

第三步,C 语言会把地址(0x867C897A)与变量名(a)相关联。


之后就可以通过 a = 1; 进行赋值操作,绕过地址。

如果你还是没听懂,我再举个例子:


第一步,查号员汇编语言会依据我们的需求为我们寻找一个我们需要联系的电话号码;

第二步,手机(C 语言)会记录下当前的电话号码,假如(135xxxx0000),方便下次使用;

第三步,手机(C 语言)会把电话号码(135xxxx0000)与你手机上的通讯录(A 先生)相关联。


这样当我作为甲方想要为 A 先生这个乙方吩咐任务时,可以直接打开通讯录,选择 A 先生,不再使用电话号码

但是直接打电话号码(内存地址)是不可以的(我也不知道为什么)


我们梳理一下,调用变量其实是通过 a -> 0x867C897A -> 0 这个过程

只是 C 语言会为我们跳过一次查找地址,从而直接访问到 0 这个值


那么如果我想要获得地址,就可以通过“&”取地址符,获得 0x867C897A

例如 printf("%x", &a);

输出结果为 0x867C897A


这一过程其实是 &a -> 0x867C897A

可以看出取地址符“&”的作用是向上查找一级


同理还有“*”取值符(注意不是int * 的 '*' ,这个只是和 int 组成 int 指针关键字)

它的作用和“&”相反,可以向下查找一级

但你不可以对一个两步后是一个非地址的变量使用“*”否则会报错

例如上面的 a,a -> 0x867C897A -> 0

是不可以用“*”的,毕竟 0 已经是确切的值了,它是不能再向下查找的


当我们为一个指针赋值,它只能赋一个地址。而且这个地址不能输入一个数字传入,必须用 & 查找

例如 int *p = &a;从而达成

p -> 指针 p 的地址 -> 0x867C897A -> 0


你不可以输入 int *p = 0x867C897A;

如果使用 (int *) 强转 C语言会再新建一个变量,存入 0x867C897A

即:p -> 指针 p 的地址 -> 未知地址 -> 0x867C897A




二、把“*”“&”玩出花


首先必须明确 C语言中的变量使用方式

例如还是 a -> 0x867C897A -> 0

如果我们直接使用 a ,C语言默认会帮我们向下查找一级,即 a = 0

而不是 a = 0x867C897A


如果我们直接使用 p ,C语言默认会帮我们向下查找两级,即 p = 0

所以这也就解释了为什么 int *p = &a 后,我们可以直接使用 p 代替 a

而不是使用 *p 去代替 a,因为

p -> 指针 p 的地址 -> 0x867C897A -> 0

a -> 0x867C897A -> 0


假如我新定义一个 int *q = &p;

那么就会得到

q -> 指针 q 的地址 -> 指针 p 的地址 -> 0x867C897A -> 0


这时printf("%x", q); 可以得到 0x867C897A

printf("%x", *q); 可以得到 0


因此我们得出了一个重要的特性!仅使用取地址符“&”修饰变量或指针只能用一次

(毕竟再往上就没了嘛......)


而取值符“*”不能直接修饰变量

(毕竟不可能对着值往下查了吧......)


不过,反复套用这两个符号还是可以的(笑)


反复使用取值符和取地址符.png


"怎么会有这么臭的代码啊!"





三、指针的妙用


通过指针,可以快速绑定任何变量进行查找、替换、修改的操作

一般网络上的大多数教程视频都会采用一个叫作“swap”的函数来举例

以此讲述指针是如何操作外部变量的


实际上,只有 C 语言才会在传入形参时使用指针

(把文件重命名为.cpp,然后使用引用大法!(笑))


指针,其实可以用于很多情况,我会在下面举例


1. 传回多个返回值


如果你用过<math.h>库,那么你应该对具备多个“返回值”的函数很了解

没错,就是分离小数和整数的 modf() 函数

它需要传入两个形参,第一个是 double 类型的小数,第二个是 double * 类型的指针

它有一个真正的返回值,可以返回内部第一个形参的小数

同时它会把第一个形参的整数部分,通过指针的方式传进第二个形参对应的变量中

相当于,把第二个变量当做了一个接收第二返回值的容器


使用modf函数.png


“通过指针 c 操作‘返回’第二个值 114514”

这个操作真的非常重要,日后如果一个写函数需要返回多个返回值,就需要灵活使用指针了

(当然你如果单片机用的 C++ 直接使用引用的话,当我没说)


2. 对数组进行增删查改


如果你写的函数需要传入一个数组作为形参时,你就一定需要指针了

如果不使用指针,那你要如何把一个局部数组给返回出去呢?

毕竟 C 语言可不能返回一个数组


当然有人可能会问,为什么我要对数组进行增删查改呢?听起来就用得不多啊

其实,用的还是挺多的。比如修改字符数组


(小声BB:不知道为什么要修改字符数组的可以看上一节的字符串与ASCII码详述


当然还是可能会有同学选择使用<string.h>库来进行字符串增改

但是这个库它贼大,包含这个库可能就会挤占掉大量的ROM内存


假如你现在用的是单片机,还是容量非常小的 STC51 单片机

也没有大容量的 EEPROM 让你通过 IIC 通信曲线救机

你要如何编写一个删改字符串的函数,以满足你的 LCD 屏函数呢?


哒不出来(雪风).jpg


所以,还是自己动手写一个这样能修改字符串的函数吧


3. 链表存储


说起链表,我就想到孔磊大佬在部内电赛做的管理系统了


这个链表出现的原因其实是因为数组或者 malloc 等数据指令导致的

假如我要存储一组未知大小的数字或信息,那么第一种方法就是首先申请一块大内存先存着

比如 int *p = (int *)malloc(1024); 挺大的吧,能存256个数字

再比如 int arr[256]; 也是一样的


但是我们并不知道要读取的文件会占用多大的空间,这256个数字到底够不够用


可能有人会想到,一块不够就再申请一块呗,肯定有够的时候吧?

但是使用这种方法(malloc),就需要一大堆的指针,为了方便管理,就需要一个指针数组

得嘞,问题又回来了。毕竟申请内存的时候肯定不能写 MAX,那样电脑就不能干别的事情了


解决这个问题的方法是采用链表的方式

也就是说,把第一段空间的最后一个字节,通过指针链接到第二段空间的第一个字节

这样系统就可以边创建,边读取了,就不需要先划定一个范围。造成读不满浪费,读满死机的问题了



当然指针的用途还有很多,有一句经典的话是这样说的:

“C语言的精髓在于指针”

说的其实一点也没错




四、小结

在本Part中:

1. 指针和其他变量不同,其他变量存储的是具体的值,而指针存储的则是值的地址;

2. 默认情况下 C语言会对普通变量进行一次地址查找,对指针进行两次地址查找;

3. 可以使用 & 减少一次地址查找,使用 * 增加一次地址查找;

4. C 语言指针被广泛用于在一个函数内返回多个值;

5. C 语言指针被广泛用于对数组进行增删查改;

6. C 语言指针被广泛用于在文件读取的链表中。


题外话:

话说真的有人看学习笔记学习嘛





好懒~~不想说~~~
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表