电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
干货|单片机的指针怎么学?
分 享
扫描二维码分享
干货|单片机的指针怎么学?
单片机
C语言
指针
果果小师弟
关注
发布时间: 2021-05-08
丨
阅读: 391
**摘要**:大家想过没有我们用keil写单片机的代码,你的函数啊、变量啊最终都放在了哪里?我们一直说的内存五区,到底是哪五区?到底放在芯片的那个地方呢?还有为什么你学完C语言指针和结构体32单片机里面的关于结构体指针的内容还是搞不清楚呢?如果你有这些问题,今天就带你研究研究! ![ ](https://img-blog.csdnimg.cn/20210507182009918.png) 这张图学过STM32单片机的小伙伴应该都不陌生,我们看到的STM32芯片已经是已经封装好的成品,主要由内核和片上外设组成。若与电脑类比,内核与外设就如同电脑上的CPU与主板、内存、显卡、硬盘的关系。芯片和外设之间通过各种总线连接。连接被控总线的是FLASH,RAM和片上外设,这些功能部件共同排列在一个4GB的地址空间内。上面这些张图是STM32F40XXX系列单片机的内存地址映射图。 我们的代码就是放在Flash里面(0x8000000~0x80FFFFF)。代码就是你写得各种函数,而在程序或者中声明的各种变量都放在RAM中,局部变量就是在函数运行完空间就释放了,全局变量就是程序运行完了再释放,可以这样简单的理解。 CPU使用的变量是存储在RAM里面的,要问我RAM是啥,RAM就是个芯片。就是上图的Block1的SRAM区。CPU是通过导线和RAM芯片连接的,然后可以通过导线往RAM芯片里面存储数据和读数据。首先RAM需要有个一开始的地址,对于STM32单片机来说开始地址是0x20000000,要问我为啥要规定地址。只有规定了地址CPU才好对数据进行存储,要是没有地址,瞎几把存,瞎几把取...... ![](https://img-blog.csdnimg.cn/20210507183716423.png) ![](https://img-blog.csdnimg.cn/20210507183509985.png) # 1、变量 1.定义了一个int型的变量,通过打印可以看到这个变量存储的地址是:0x20000000。这也证明了我们内存的首地址是0x20000000。我们定义的value变量就放在这里。 ![](https://img-blog.csdnimg.cn/20210507122451320.png) ![](https://img-blog.csdnimg.cn/20210507122404683.png) 2.再定义一个变量 通过打印可以看到这个变量存储的地址是:0x20000004。因为int类型在内存中占据4个字节,所以第二个变量就存放在0x20000004这个地方。 ![](https://img-blog.csdnimg.cn/20210507122628133.png) ![](https://img-blog.csdnimg.cn/20210507122608898.png) 综上所述,定义的两个变量在内存里面是下面这样子。 0x2000 0000地址里面存储的是 0 0x2000 0004地址里面存储的是 1 ![](https://img-blog.csdnimg.cn/20210507200748324.png) # 2、指针变量 定义指针其实和定义变量一样一样的,只不过变量名前头有个* 下面就定义了一个int型的指针变量,变量的名字是p。然后有人会问,为啥变量名字前面加个*就是指针了? 答:搞C语言那帮家伙们规定的。 定义指针和定义变量一样哈,然后可以定义各种类型的。 然后记住一句话: >指针这个变量是存变量的地址的! 指针这个变量是存变量的地址的! 指针这个变量是存变量的地址的! 所以给指针赋值自然是把变量的地址给它。 ```c #include "sys.h" #include "led.h" #include "delay.h" #include "usart.h" int value = 0; int value2 = 1; int *p; int main(void) { uart_init(115200); delay_init(); p=&value;//把变量value的地址复制给这个指针 printf("Address of a: %p\n",p);//打印下这个指针指向的地址 while(1) { } } ``` ![ ](https://img-blog.csdnimg.cn/20210507122953248.png) ![ ](https://img-blog.csdnimg.cn/20210507122920856.png) 一般什么类型的指针变量就应该赋值什么类型变量的地址。如再定义个char型 ```c #include "sys.h" #include "led.h" #include "delay.h" #include "usart.h" int value = 0; int value2 = 1; int *p;//定义一个指针 char value3=1; char *q; int main(void) { uart_init(115200);//串口初始化 delay_init(); p=&value;//把变量value的地址复制给这个指针 q=&value3;//把变量value的地址复制给这个指针 printf("Address of a: %p\n",q);//打印下这个指针指向的地址 while(1) { } } ``` 那些规定C语言的大佬弄出来指针这个玩意有啥用? # 3、指针有啥用? 1.咱先使用感受下指针,然后具体有啥用就自己体会了。前面咱把一个变量的地址赋值给了指针了,然后搞C语言的那帮家伙们又规定。*{指针变量名} :代表了这个指针所指向的变量。 啥意思呢? 对照下面的程序 p=&value, p记录的就是变量value的地址, 然后*p就代表value。 ```c #include "sys.h" #include "led.h" #include "delay.h" #include "usart.h" int value = 0; int *p;//定义一个指针 int main(void) { uart_init(115200);//串口初始化 delay_init(); p=&value;//把变量value的地址复制给指针变量p printf("Address of a: %d\n",value); printf("Address of b: %d\n",*p); while(1) { } } ``` ![](https://img-blog.csdnimg.cn/20210507123952947.png) ![](https://img-blog.csdnimg.cn/20210507123847457.png) 有人会想......就这? 这不是脱了裤子放屁 多此一举? 其实我一开始也是这样想的...... 既然 * p就代表value 那么 * p=XXXX,不就是相当于value=XXXX ![](https://img-blog.csdnimg.cn/20210507124033112.png) 看看下面这个例子 ```c #include "sys.h" #include "led.h" #include "delay.h" #include "usart.h" int value = 0; int *p;//定义一个指针 int main(void) { uart_init(115200);//串口初始化 delay_init(); p=&value;//把变量value的地址复制给指针变量p printf("value of a: %d\n",value); *p=520; printf("value of b: %d\n",value); while(1) { } } ``` ![](https://img-blog.csdnimg.cn/2021050712424781.png) ![](https://img-blog.csdnimg.cn/20210507124203471.png) 还是没感觉到指针有啥用?别着急,先把基本的知识点学完哈。没有最基本的知识储备是不可以的,因为厚积而薄发! 见过返回值是指针的函数没? # 4、函数指针 先看一下,如果感觉不理解就接着往下看 ```c #include "sys.h" #include "led.h" #include "delay.h" #include "usart.h" int value = 0; int *p;//定义一个指针 int *function(void) { return &value;//把value的地址返回 } int main(void) { uart_init(115200);//串口初始化 delay_init(); p=function();//调用函数,其实就是把value的地址赋值给了p printf("Address1 of a: %p\n",&value);//打印value的地址 printf("Address2 of a: %p\n",p);//打印p所代表的地址 while(1) { } } ``` ![](https://img-blog.csdnimg.cn/2021050712511798.png) ![](https://img-blog.csdnimg.cn/20210507125144224.png) 很多人用过返回值是int、char 等等的函数,但是在int,char 后面加个* 估计对于初学者没有用过。看下面的,其实就是指针之间赋值。下面就是把p(int*类型的指针) 代表的地址赋值给q 变量之间可以互相赋值吧,指针之间也一样,可以互相之间赋值。 其实和上面是一样的道理,那个函数function返回值是一个int*类型的指针,然后赋值给了p而已 ```c #include "sys.h" #include "led.h" #include "delay.h" #include "usart.h" int value = 0; int *p;//定义一个指针 int *q;//定义一个指针 int main(void) { uart_init(115200);//串口初始化 delay_init(); p=&value;//把value的地址赋值给了p q=p;//把p代表的地址给q printf("Address1 of a: %p\n",&value);//打印value的地址 printf("Address2 of a: %p\n",q);//打印p所代表的地址 while(1) { } } ``` ![ ](https://img-blog.csdnimg.cn/20210507125555266.png) ![ ](https://img-blog.csdnimg.cn/20210507125510809.png) 姑且再问一句,函数名字是啥? 咱们都知道这样调用函数 ```c #include "sys.h" #include "led.h" #include "delay.h" #include "usart.h" void function() { printf("zhiguoxin\n"); } int main(void) { uart_init(115200);//串口初始化 delay_init(); function(); while(1) { } } ``` ![](https://img-blog.csdnimg.cn/2021050712581324.png) ![](https://img-blog.csdnimg.cn/20210507125740573.png) 但是这样的见过没 ```c #include "sys.h" #include "led.h" #include "delay.h" #include "usart.h" void (*fun)(); void function() { printf("zhiguoxin\n"); } int main(void) { uart_init(115200);//串口初始化 delay_init(); fun = function; fun(); while(1) { } } ``` ![](https://img-blog.csdnimg.cn/20210507130101107.png) ![](https://img-blog.csdnimg.cn/20210507130149390.png) 这里采用了函数指针 先记住一句话 >函数名就是这个函数的地址! 函数名就是这个函数的地址! 函数名就是这个函数的地址! **既然是地址,那么这个地址应该可以赋值给一个指针。因为是函数的地址,所以咱定义的指针也一定是一个函数类型的。** 上面的函数void function()是一个没有返回值,没有形参的函数。那么咱需要定义一个这种的指针类型,其实就是void (*指针变量名字,随意写) ()。上面写的是 void (*fun)(); fun就是一个函数类型的指针,是一个没有返回值,没有形参的函数指针。 咱可以把这种函数赋值给这个指针变量。就是上面的fun=function。那么这个函数指针便代表了那个函数fun就等同于function。所以调用 fun(); 就等同于调用function()。 如果函数有形参怎么办? 好办,它有咱就加 ```c #include "sys.h" #include "led.h" #include "delay.h" #include "usart.h" void (*fun)(int a); void function(int value) { printf("value=%d\r\n",value); } int main(void) { uart_init(115200);//串口初始化 delay_init(); fun = function;//把function赋值给fun fun(520);//fun就等同于function while(1) { } } ``` ![](https://img-blog.csdnimg.cn/20210507130619794.png) ![](https://img-blog.csdnimg.cn/20210507130708330.png) 如果函数有返回值怎么办?照加不误 ```c #include "sys.h" #include "led.h" #include "delay.h" #include "usart.h" int res; int (*fun)(int a); int function(int value) { return value; } int main(void) { uart_init(115200);//串口初始化 delay_init(); fun = function;//把function赋值给fun res = fun(520);//fun就等同于function printf("res=%d",res); while(1) { } } ``` ![](https://img-blog.csdnimg.cn/20210507131051620.png) ![](https://img-blog.csdnimg.cn/20210507131136103.png) 小总结一下 指针呢其实基本的也就是上面那些,指针就是用来记录变量的地址的。或是做地址之间的传递的。 >&代表取地址符。 *代表取数据。 &{变量名} :就是把这个变量的地址取出来。 *{指针变量名} :就是把这个指针所代表的地址里面的存的值取出来 下面看一些比较常见的应用。把数组的地址赋值给指针,然后用指针操作数组 ```c #include "sys.h" #include "led.h" #include "delay.h" #include "usart.h" char temp[3]={1,2,3}; char *p; int main(void) { uart_init(115200);//串口初始化 delay_init(); p=temp;//将数组名赋值给指针变量p,p就指向数组temp的首地址 printf("value0=%d\r\n",*p); //p就代表数组的第一个数据的地址 printf("value1=%d\r\n",*(p+1));//p+1就代表数组的第二个数据的地址 printf("value2=%d\r\n",*(p+2));//p+2就代表数组的第三个数据的地址 printf("temp[0]=%d\r\n",p[0]);//p[0]等同于temp[0] printf("temp[1]=%d\r\n",p[1]);//p[1]等同于temp[1] printf("temp[2]=%d\r\n",p[2]);//p[2]等同于temp[2] while(1) { } } ``` ![](https://img-blog.csdnimg.cn/20210507131920988.png) ![](https://img-blog.csdnimg.cn/2021050713183954.png) # 5、函数的形参是一个指针 ```c #include "sys.h" #include "led.h" #include "delay.h" #include "usart.h" char temp[3]={1,2,3}; void function(char *value) { printf("value0=%d\r\n",value[0]); printf("value1=%d\r\n",value[1]); printf("value2=%d\r\n",value[2]); } int main(void) { uart_init(115200);//串口初始化 delay_init(); function(temp); while(1) { } } ``` ![](https://img-blog.csdnimg.cn/20210507132246151.png) ![](https://img-blog.csdnimg.cn/20210507132204141.png) 以上的指针的基本知识多练习几遍就可以。指针真正的应用是在于代码的封装。可能对于初学者感受不到其作用,但是当你成为真正的开发人员。你会发现把好多功能函数封装起来,然后留出接口来调用是以后必不可少的。 封装的时候会大量的使用指针,函数指针、结构体指针等,怎么说呢!90%的程序员敲的是字母,写的是代码。当你开始封装的时候,你写的便是思想,但是需要一定的基础知识储备才能达到。 ![](https://cf03.ickimg.com/bbsimages/202105/f0897ad7107e4358fbf74aa3a8040997.png)
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
果果小师弟
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交