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等等;而另外一种呢,没非就是集程序下载、调试功能于一体的编程器、这个叫法比较混乱、也能够叫编程器、下载器、仿真器(但我认为不叫烧录器,烧录器一般值批量烧录阶段所用的那种能够快捷完成芯片批量烧录的玩意)。后文统称编程器。 |