打印
[资源共享]

C语言指针用法及实际应用详解

[复制链接]
718|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
豌豆爹|  楼主 | 2023-3-27 15:11 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
c语言指针用法及实际应用详解,通俗易懂超详细!

\\\插播一条:文章末尾有惊喜哟~///

今天给大家来讲解一下指针。

我会由浅到深,最后联合实际应用讲解,让大家学会指针的同时,知道大佬们都用指针来干嘛!

长文预警!全文大约5200多字,学指针看这篇文章就够了!

很多人跟我刚进修c语言一样,都怕指针。

我也是后面做了一些物联网网关才知道,指针是c语言的灵魂这句话真正含义。

没有指针,很多功能实现起来的确很不方便,假如做不到真正的模块化编程。

Ok,废话不多说,下面正式进入主题。

一、通过这篇文章你能掌握以下知识:

00001.指针的相关概念

00002.掌握指针与数组之间的关系

00003.掌握指针指向的指针

00004.掌握如何使用指针变量做函数参数

00005.掌握如何使用指针函数

00006.掌握如何使用数组指针函数

那么这篇文章对应有视频教程,假如不喜爱看文章的能够去看视频,教程在小破站能够搜没际单片机编程找到,也能够找我们拿。

二、指针的作用:

指针是C语言中一个比较重要的东西,有人说指针是C语言的灵魂这句话说的一点也没错。

正确灵敏地运用它,能够有效地表达一些复杂的数据构造,假如系统地动态分配内存、音讯机制、任务调度、灵敏矩阵定时等等。

掌握指针能够使你的程序愈加简洁、紧凑、高效。

那么在单片机领域,假如是做稍微大一点的项目,须要把每个功能做成模块化,硬件驱动层和应用层分别独立运行。

即便更换单片机型号也不用修改应用层程序,即移植性非常强,这些都离不开指针。

甚至没指针会很难实现,即便实现代码的可移植性也很差。

三、指针的概念

前面讲了指针的作用,这里再强调一点,指针是一把双刃剑。

用好了能十分灵敏而且提高程序的效率,但是假如使用不当,则会出现程序”死机”等致命问题。

而这些问题往往是由于错误地使用指针而造成的,最常见的就是内存溢出错误,指针指向未知地址。

1.地址与指针

指针是一个比较抽象地概念,假如想真正了解指针,那么要先从数据是如何存储的说起,我们通过一个图来看一下数据在内存里存储的情况。

在这个图中,都是以16进制显示。

红色标注的0x00000400代表地址内存地址,绿色37,30代表数据,而橙色标注的00 01代表地址递增量,即代表0x00000400和0x00000401,每个地址存储1个字节数据。

那么我们把这个图看作是数据在内存里的存储形式,0x00000400这个内存地址存储着数据37,0x00000401这个内存地址存储着数据30。

当我们在程序里定义一个字节的变量,那么在编译器编译时就会给这个变量分配一个这样的内存地址来存储。

假设我们定义以下变量

unsigned char a;

a = 0x37;

对应这个图就是,编译器在编译时会为变量a分配一个字节的内存空间并把0x37这个数据存储进去,并将变量名a改成地址0x00000400,以便CPU的访问。

通过这个地址就能找到变量a数据的存储位置,而这个地址0x00000400其实就是指针,通过这个指针能够访问变量a的数据。

2.指针变量

通过上面讲解我们明白了通过地址能访问内存的数据,这个地址啊就是指针。

那么指针和指针变量呢是不一样的概念,大家一定要记住了。

指针是概念、指针变量是这个概念的详细应用之一,我们先来看一下C语言里怎么定义指针变量。

指针变量定义的一般形式:

变量类型*变量名

unsigned char *p;

通过这种语法,我们就能够定义一个指针变量p。

指针变量赋值

指针和指针变量是两个概念,指针变量跟普通变量一样,在使用前一定要定义和赋值(指向地址)。

给指针变量赋的值和普通变量不同,给指针变量赋值只能赋地址,而不能赋予其他任何值,否则会引起错误。

那么怎么获取普通变量的地址呢,在C语言里能够使用”&”来获取普通变量的地址,一般用以下格式来表示:

&变量名

那么通过&变量名取得变量地址后就能够赋值给指针变量。

举例:

unsigned char a;

unsigned char *p

int main()

{

p = &a;
}

这个代码里,我们定义了一个变量a,定义了一个指针变量p。

我们通过运算符&把变量a的内存地址赋值给变量p,所以p指向了变量a的内存存储地址。

上面说了指针变量赋值的问题,那么怎么获取和变更指针变量指向那个内存地址的数据呢?我们能够通过:

*指针变量 =数值

如:*p = 10;

这样的操作能够变更指针变量指向那个内存地址的数据。

通过:

a = *p;

来获取指针变量指向那个内存地址的数据。

下面我们通过一个代码实验来举例。

这里我们定义了变量a和指针变量p,然后a的值初始化为10。

把a的地址赋值给指针变量p,接着我们输出a的地址是0x60ff33。

由于前面我们把a的地址赋值给了指针变量p,所以p指向的地址也是0x60ff33。

那么我们再来看一下,指针变量的在内存里的存储地址是0x60ff2c。

所以大家这里要注意了,我们定义指针变量时,即便指针变量是指向地址用的,但是编译器也会分配一块内存地址来存储指针变量。

我们接着来看下变量a的输出值。

a=10, *p是获取指针指向内存地址的数据,所以也是10。

下面就是通过指针变量来变更变量a的值,由于指针变量p指向的是变量a的地址,所以变更指针变量p指向内存地址的数据就能够变更变量a的值。

那么通过这么原理,我们是不是不用指针变量,也不用a即是多少来变更a的值呢?当然能够!

我们看下面通过内存地址变更变量a的值,我们前面知道a的地址是0x60ff33,那我们能够直接写0x60ff33=12来变更变量a的值。

当然这里要注意,编译器编译时并不知道0x60ff33是什么东西,所以要把这个整形地址转换成指针类型。

最后通过*+地址语法变更这个地址里面的数据。

我们看输出结果,能够发现a的值已经成功被改成了12。

其实通过指针变量变更某个内存地址的数据就是这个原理,但是指针变量益处能够任意起名字。

也不用像这样先把变量a的地址读出来,然后通过地址去变更它的值,用起来就很方便,所以通过指针变量来替代了这种做法。

四、数组与指针

一般系统或编译器会分配不间断地址的内存来存储数组里的元素,假如把数组地址赋值给指针变量,那么就能够通过指针变量来引用数组,读写数组里的元素了。我们来做个实验:

从这个代码来看,定义了一个数组buff并初始化为1,2,3,4,5。

定义了2个指针变量p1和p1,分别指向buff, &buff[0]。

buff默认的是数组下标为0元素的存储地址。

所以这里buff和&buff[0]是同一个内存地址,只是写法不一样。

我们从输出结果能够看的出来,数组和指针变量的地址都是一样的,所以大家用这几种写法都是能够的。

那么我们来看下输出结果,都是1,说明操作是对的。

指针自加自减运算

指针变量除了能够用来获取内存地址的值以外,还能够用来进行加减运算。

但是这个加减呢跟普通变量加减不一样,普通变量加减的是数值,而指针变量加减的是地址,我们来通过代码来讲解下。

同样这里定义了数组buff并初始化为1,2,3,4,5。

我们把指针变量p1指向数组第一个元素的地址,即0x402000。

然后我们直接看p1++的操作,p1++后我们看到p=0x402001,所以指针变量的加减等运算是指向地址的运算。

其他减法乘除法也是基于地址的运算。

如何攻克单片机下载程序失败的问题

作为一个单片机开发人员,每天跟我们打交道的工具没非两种,一种是PC机上的开发环境,假如Keil MDK、IAR Embedded Workbench等等;而另外一种呢,没非就是集程序下载、调试功能于一体的编程器、这个叫法比较混乱、也能够叫编程器、下载器、仿真器(但我认为不叫烧录器,烧录器一般值批量烧录阶段所用的那种能够快捷完成芯片批量烧录的玩意)。后文统称编程器。

使用特权

评论回复
沙发
豌豆爹|  楼主 | 2023-3-27 15:11 | 只看该作者
常见的编程器有以下几种:

1、自然是老牌Jlink了;

其实拆开市面上常见的jlink,能够看到各种各样的板子:

这都是从某宝上买的,标注着正版、永不掉固件的jlink,这样的东西在我手里坏了2个,一初始我还有毅力去找个XP系统刷一下固件,但是刷了没几天就又坏了,而且身边的XP是越来越少了,想着再买一个也才几十块,完全没必要浪费这个时长去刷了。当然很多人喜爱动手挑战一下,但是当你的项目忙的不可开交,而这个时候你的编程器固件掉了,你还要跑去找个XP给他刷固件,想想都不爽。

有朋友告诉我,自己经常要去其他城市出差,调试自己的板子,SEGGER的原版太贵,只好从某宝买几十块的用,而且买一个又担忧坏了耽搁事,所以每次出差至少都带两个,然而这个硕大的体积是在让人不爽,出差的时候要在极其宝贵的行李空间里放两个jlink,也是够了。

也有人埋怨,jlink的接口太不方便了,虽然大多数时候调试的时候,只用了四五根杜邦线截出来,然而他的接口却保留了20脚,其中有一整排除了一个脚全是GND了。做个板子,入股只留几个排针,那样不好插线调试,假如想留一个和jlink一样的接口,又觉得会显得板子笨重,而且在一些对尺寸有严格要求的产品里,压根没有空间放下这个20脚的接口。

这时候有人提出了攻克方案,也就是下文的第二种编程器:

2、ST-LINK

可是仔细看看这个并没有攻克JLINK的一些缺少点,接口还是笨,体积还是不小。那么万能的某宝上还有一种攻克方案;

各种各样的颜色,而且价格比较便宜,十五块左右就能搞到,尝鲜买个试试,发现挺好用。和优盘一样大小的身板,出差带着也方便。但是某一天它忽然不工作了,插上之后电脑没有人任何反应,我经过一系列的诊断,最后怀着沉重的心情给它下了死亡通知书。

然后再去买下载器,我又不想买太贵的,又想买个小体积的,想来想去,最终一次买了三四个这种小的STLINK,我知道他会坏,但是一次买好几个就不怕他坏了,而且好几个的价格也就是一个某版的jlink价格。

接下来没意发现了淘宝上有一款叫做elink的玩意:

3、elink32-pro

这个东西看起来很棒,由于它集成了串口调试,而且貌似速度很快,正好我和合泰半导体这个公司有业务往来,就请合泰的相关负责人寄我一个样品试用,试用效果很棒,唯二的缺少点就是体积太大(和jlink差不多)、价格太贵。

那么有没有可能和合泰达成一个协议,共有作出一款合乎以下需求的编程器(下载器、仿真器、调试器)呢?

a、能支持简直所有32位ARM-CortexM内核单片机

b、小巧便携

c、不掉固件

d、去掉多余接口,只保留swd

e、增加虚拟串口功能

f、可对外供电、电流过大自动切断

g、低老本,大多数人买得起

h、没需驱动,即插即用

于是我和合泰的相关人员进行沟通(这个沟通大略是16年10月初始的)。

经过屡次沟通,最终确定:以elink32-pro为原型,去掉一些没用的东西、增加相关功能、由合泰半导体提供固件,我提供硬件及出产。

沟通中争议最多的一个问题是RST引脚是否保留。由于我们使用stlink,jlink等时经常会发生以下问题:

这个问题能够通过一下方式攻克:

但是还有很多问题是通过开发环境配置修改所不能攻克的,这时候就须要保留RST,然后把RST接在开发板上,就能完成下载,这个功能主要是波及以下几种使用场景:

1、swd引脚复用(例如STM32F103的PA13和PA14在程序中被设置为其他功能),这时候须要编程器能对开发板进行硬件复位。

2、芯片工作状态为低功耗或者睡眠模式,这时候须要编程器能对开发板进行硬件复位。

所以最终探讨结果是依照合泰半导体厂商提供的方案来,毕竟他们是半导体厂商,考虑问题很严谨、完善。

方案定稿之后,签订了软件使用合同与保密合同,接下来是合泰提供带固件的芯片,我来做整体设计并完成PCB设计、焊接调试等详细的工作。经过一个月的时长、出了几个样品,经过测试发现使用不顺手,等各种问题。假如第一次构思的是直接在板子上加一个usb公头,然后就能够插在电脑上了,但是后续使用发现,这样不是很方便,由于这样的话就会导致宗旨板距离电脑独有杜邦线长度的距离,假如杜邦线太短,挪动不太方便,甚至此时有些电脑的USB口在后面,这样不方便用户下载程序后观察现象。而杜邦线太长则会不稳定,很多情况下,程序没法下载是与杜邦线连接不稳定有关系的(在使用优盘状的STLINK时,经常由于杜邦线太长或者接触不良导致下载失败,后来换成航空线直接焊接上去就能够下载,说明这种stlink的io口驱动才能欠佳)。

考虑以上的缺少点,我决定使用此时最常见的micro-USB线来连接e-Link32和PC机。于是经过改版,e-Link32变成了下面这样(手工焊接调试版):

其中白色的SH1.0端子和右侧的5P弯针都是SWD接口。为啥要增加一个SH1.0端子呢,由于在有些电路里,保留2.54的插针都是对空间的浪费,而且插针是要占用PCB的双面,对其他布线造成了影响。所以增加了SH1.0端子接口,以后大家做板也能够使用这样的端口、减小板子面积,方便调试。

这个版本e-Link32我赠送给五个人进行测试,大略是从16年12月份初始测试的、到17年8月份没有人任何问题,所以我才批量了1000个。选用的是最可靠的PCB提供商和贴片厂商,做出的板子如下:

一个产品必需有完善的文档。在量产的阶段我也进行了相关文档的编写

详细文档能够在淘宝宝贝详情下载。大略详细内容如下:

另外清晰的阐述了elink32如何连接开发板和电脑:

以及如何用elink32给带20脚jtag接口的开发板下载程序:

此时能够说完成了当时提出的这个需求,实现了具有以下功能的调试器:

经测试,可用于但不局限于以下芯片的调试:

STM32F0,STM32F1,STM32F2,STM32F3,STM32F4,STM32F7

STM32L0,STM32L4

LPC800XX,LPC1100XXX,LPC1200XXX,LPC1300XXX,LPC1500XXX,LPC1700XXX,LPC1800XXX,LPC4000XXX,LPC4300XXX,LPC5400XXX,

K60

HT32F16XX,HT32F52XXX

NV32F100XXX

EFM32XXX

W7500

列表有待完善。

有人提出了:带串口的东西其实早有了,那么你能够去看看,网络上也有很多教程教大家做这样的下载器的,我能够花一两天时长依据教程完成这么一个下载器,但我肯定没有勇气把他批量了放在市场上给大家用。一款工具性的东西须要经过长期的稳定性测试、还须要对加工工艺进行测试、这些流程都增加了产品的老本,但是却是保证用户买到有保证的产品的一个必需的投入。

兼容市面上绝大多数ARM Cortex-M系列单片机。

500ma3.3v对外供电电流,加自恢复二极管保驾护航。

半导体原厂提供固件与技术支持。

一个USB口完成下载调试串口通讯,一遍单步一遍查看串口数据,为你的电脑节省一个宝贵的USB口。

从16年测试,到17年初始量产,此时已经销售了3000多个了,经测试非常稳定。

使用特权

评论回复
板凳
Pretext| | 2023-4-13 10:16 | 只看该作者
C最厉害的就是指针,最难的也是指针。

使用特权

评论回复
地板
天天向善| | 2023-4-13 10:16 | 只看该作者
会指针之后,发现别的语言怎么没有指针。

使用特权

评论回复
5
软核硬核| | 2023-4-13 10:18 | 只看该作者
指针的使用方式真的是千变万化,总会遇到不同的使用方式。

使用特权

评论回复
6
七毛钱| | 2023-4-25 10:50 | 只看该作者
指针的使用也是很灵活了

使用特权

评论回复
7
七毛钱| | 2023-4-25 10:50 | 只看该作者
但也很难学,哈哈

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

513

主题

1960

帖子

5

粉丝