摘要:听说还有好多学单片机的小伙伴不会用结构体?指针和结构体是学单片机必须要掌握的,如果你C语言掌握的不牢,单片机根本学不到精髓,只能完成一些低级的项目。看得懂结构体并且能够灵活运用结构体才能说你入门了单片机。本篇将以最通俗的方式结合STM32单片来讲讲结构体的运用。解决你学完C语言、考过了计算机二级还是看不懂单片机结构体的苦恼。宝藏文章,记得点赞转发收藏。
大家知道指针和结构体是单片机的难点,所以就去学习C语言,找视频看书......
![](https://i-blog.csdnimg.cn/blog_migrate/6620dae3b95165be05d3f3a7ee9c70b7.png)
这里面每一个视频的播放量都非常高。对于单纯的学习C语言,这里讲的很清楚。看完你不禁在下面评论一句:哇!讲的真的太清楚了吧!但是等你真正的学单片机的时候,你会发现我不是学过C语言吗?计算机二级我也过了啊!怎么这个指针和结构体都不懂啊?难道我学了一个假的C语言?
![](https://i-blog.csdnimg.cn/blog_migrate/7a28dd215631459b7c58a4a07d0a96ae.jpeg)
其实这不是你的错,也不是单片机的错,而是在C语言和单片机之间需要一个过渡!这个需要过渡的点在很多单片机视频教程中并没有去讲解。因为教育机构默认你是知道的,所以在讲流水灯时他们并不会讲解GPIO初始化这个结构体,因为默认你是知道如何操作的。
![](https://i-blog.csdnimg.cn/blog_migrate/2ecdbf31418799c32fea776632451075.png)
申明一个GPIO_InitTypeDef
的结构体,然后在LED_Init(void)
函数中定义一个GPIO_InitStructure
的变量GPIO_InitStructure
,那么这个变量就可以设置这个GPIO_InitTypeDef
的结构体中的成员。这里先做了解,请接着往下看。
1、为什么需要结构体
这里先不说什么是结构体,说说为什么需要结构体?只有知道为什么需要,才能按照你的需要去学习,这样效率才会高。你才知道在什么情况下我们需要写一个结构体,怎么样去用结构体。
这里我们以一个智能家居的项目为例。
先来看一个实际的问题
话说有一个项目上有4个传感器:光照传感器、烟雾传感器、酒精传感器、湿度传感器。然后这四个各个传感器还有设置报警的阈值范围。
一般都是这样写
#include "sys.h"
#include "delay.h"
#include "usart.h"
/*记录传感器的数值*/
float temperature; //温度
char humidity; //湿度
char alcohol; //酒精浓度
int illumination; //光照强度
/*记录传感器高低阈值*/
float temperature_threshold[2];
float humidity_threshold[2];
float alcohol_threshold[2];
float illumination_threshold[2];
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
while(1)
{
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/d0c4734c466ab088a862110489d6e025.png)
当然你做一个项目肯定还定义了很多其他的变量,还需要记录其它变量
![](https://i-blog.csdnimg.cn/blog_migrate/f10c1f576413a2d83232f0bd19090cc4.png)
然后过了几天又增加了个一氧化碳传感器
![](https://i-blog.csdnimg.cn/blog_migrate/d621d45e6526b5cb825cdcd43ae546ec.png)
然后过了几天,每个传感器还需要加个是否正常工作的标志位
![](https://i-blog.csdnimg.cn/blog_migrate/802a81ab0e566214ad5ee7e076bd7f0c.png)
因为项目的需要,然后又增加了4个相同的传感器:温湿度、光照强度、烟雾浓度、酒精浓度。
![](https://i-blog.csdnimg.cn/blog_migrate/6def3065a940c37dcf79036ef6cd5d39.png)
然后又增加了4个相同的传感器:温湿度、光照强度、烟雾浓度、酒精浓度。
截图截不开了....
![](https://i-blog.csdnimg.cn/blog_migrate/ef6a4abef7a63d3f9130d5fda68d7427.png)
满屏的变量......
满屏的变量......
满屏的变量......
在项目刚开始做的时候如果不能未雨绸缪,接着干下去整个程序代码别说维护了,就是接着写都让人头疼!
满屏的变量...
满屏的变量...
2、结构体闪亮登场
然后搞C语言那帮家伙就造了个功能struct
1、结构体就是可以把变量包含到里面的东西
struct就代表要定义一个结构体,sensors是这个结构体的名字, 然后是一个大括号 { }
大括号里面就随意定义变量啦~
![](https://i-blog.csdnimg.cn/blog_migrate/cfcef3b672a2d99fbc866a6bfa9b33c9.png)
怎么使用里面的变量呢?
注意结构体是一个数据类型就像是int和char一样的这种类型
既然是一种数据类型, 那么就可以用这个数据类型定义变量
定义一个该结构体的变量
![](https://i-blog.csdnimg.cn/blog_migrate/65d77b16946454750a9727a9389fe433.png)
为啥要那样子定义啊?
答:你去问造C语言的那帮家伙去!问问他们为啥要设计成这样子!
然后操作结构体变量里面的成员变量。当我们定义好结构体变量后,在初始化变量里面的成员变量时就会自动出现结构体里面的成员变量,如果这个代码是你一个一个敲出来的话,你就会感叹结构体在单片机中是那么的奇妙!
![](https://i-blog.csdnimg.cn/blog_migrate/33e997485351219162585dfb729a1156.png)
![](https://i-blog.csdnimg.cn/blog_migrate/51f46353eb7c2c24551e21b2c381992f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5d2538ac8fccfbdb8bf4ac3263734202.png)
有人会问为啥是结构体变量中间加个点?
答:你去问造C语言的那帮家伙去!问问他们为啥要设计成这样子。
2、其实定义结构体变量可以下面这样子
![](https://i-blog.csdnimg.cn/blog_migrate/61142dfcce35b477f123ff3065ad81bd.png)
也可以定义多个
![](https://i-blog.csdnimg.cn/blog_migrate/6e51f593ecab3c5dc3c0b171786e8eab.png)
![](https://i-blog.csdnimg.cn/blog_migrate/01761508f5ff6f97d5b675cf7bf21e1a.png)
发现了没,每个结构体变量都是单独拥有结构体里面的全部成员变量。
就像是最开始说的,如果再增加一套传感器:温湿度、光照强度、烟雾浓度、酒精浓度。
使用结构体的话只需要再定义一个结构体变量即可。
但是很多时候我们在单片机中见到的结构体并不是上面那样定义的,而是在前面加了一个typedef 关键字。
这样的例子在库函数的头文件中我们经常会看到如下结构体
![](https://i-blog.csdnimg.cn/blog_migrate/83737675ce3c2e11da62a20d7ebd7ca3.png)
3、typedef关键字
先看一下百度百科对typedef
的定义
![](https://i-blog.csdnimg.cn/blog_migrate/f35a43c97217f34dfc377e652acc0e73.png)
总结一句就是:typedef
可以把一个数据类型取一个别的名字
typedef {数据类型} {别的名字}
#include "sys.h"
#include "delay.h"
#include "usart.h"
typedef int zhjiguoxin;//zhjiguoxin就是int
zhjiguoxin value = 0;
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
printf("value=%d\r\n",value);
while(1)
{
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/2f39c593315fe0310ed0c7d345ade75e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f9bc5b60a0673ab2a92ddc8a1dbc17d4.png)
虽然typedef可以给变量取别名,但是没有谁会像上面那样取名字,我这里只是举一个例子。
4、结构体的精髓
注意下:
1、下面的代表了这个结构体数据类型
![](https://i-blog.csdnimg.cn/blog_migrate/b796ffe61ede15bbf782ba70a01b7a8a.png)
2、给这个数据类型起一个别名
注意是三部分, typedef {数据类型} {别的名字}。所以sensor就代表了这个结构体了。
建议初学者把下面这张图保存到你的电脑,这样你就永远也不会忘记typedef在结构体中的用法了,也能很快的记住结构体这个东东。
![](https://i-blog.csdnimg.cn/blog_migrate/7e96d71f407d83a488259874c7bbd07b.png)
3、以后定义结构体变量的时候就不需要像最开始那样struct sensors sen;这样的定义结构体变量了,只需要sensor sen;即可。
![](https://i-blog.csdnimg.cn/blog_migrate/fd4ce1e5cab997d06df2e15f048e41cc.png)
4、结构体名字可以省略
注意结构体定义可以不写结构体名,对C语言来说,那个sensors不叫结构体名,而是叫标签(tag)。C语言结构体名是struct关键字 + tag。所以为了简便我们看到的单片机中的结构体都是写成如下的形式。
![](https://i-blog.csdnimg.cn/blog_migrate/7b173ac88b00126b6c84d3170b400709.png)
5、结构体的变量可以放任何变量
1、结构体变量可以放任何变量(int型指针)
#include "sys.h"
#include "delay.h"
#include "usart.h"
typedef struct
{
float temperature; //温度
char humidity; //湿度
char alcohol; //酒精浓度
int illumination;//光照强度
char CO; //一氧化碳浓度
int *p; //int型的指针变量
} sensor;
sensor sen;
int value =0;
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
sen.p=&value;//把value的地址赋值
//打印p代表的地址里面的值(其实就是打印value的值)
printf("value=%d\r\n",*(sen.p));
while(1)
{
}
}
既然是指针变量,所以给指针变量赋值时当然是赋值的是一个地址。
2、结构体变量可以放任何变量(函数指针)
#include "sys.h"
#include "delay.h"
#include "usart.h"
typedef struct
{
float temperature; //温度
char humidity; //湿度
char alcohol; //酒精浓度
int illumination;//光照强度
char CO; //一氧化碳浓度
int *p; //int型的指针变量
void (*fun)();
} sensor;
sensor sen;
void function()
{
printf("zhiguoxin\r\n");
}
int value =0;
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
sen.fun=function;
sen.fun();
while(1)
{
}
}
既然是函数指针变量,所以给函数指针变量赋值时当然是赋值的也是地址,并且还要是一个函数的地址,而一个函数的函数名就是该函数的地址。所以才会有下面的把函数function();的地址function赋值给函数指针fun。这样大家是不是很清楚了。如果不清楚建议看个3遍以上!
![](https://i-blog.csdnimg.cn/blog_migrate/672e07fd8f28821cf63595d8b432bb6a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/645f9f0f1ab43754c777fa5c53261d79.png)
3、结构体变量可以放任何变量(结构体变量)
这就是结构体嵌套,在一个结构体内包含了另一个结构体作为其成员。当出现结构体嵌套时,必须以级联方式访问结构体成员,即通过成员选择运算符逐级找到最底层的成员时再引用。
#include "sys.h"
#include "delay.h"
#include "usart.h"
typedef struct
{
int i;
}zhiguoxin;
typedef struct
{
float temperature; //温度
char humidity; //湿度
char alcohol; //酒精浓度
int illumination;//光照强度
char CO; //一氧化碳浓度
int *p; //int型的指针变量
void (*fun)();
zhiguoxin guougo;
}sensor;
sensor sen;
int main(void)
{
uart_init(115200);//串口初始化
delay_init();
sen.guougo.i=100;
printf("i=%d\r\n",sen.guougo.i);
while(1)
{
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/46004ebe69ff87aa7d871caf895e58a5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/083e9499a2ecfb30474938ccc0372d16.png)
4、结构体变量可以放任何变量(结构体指针)
结构体是一个数据类型。数据类型当然也可以定义对应的指针变量啦。
就像是int 类型可以定义 int *p; 一样
![](https://i-blog.csdnimg.cn/blog_migrate/d12195216e5fe3e3413ffa03ce9bcf04.png)
所以当大家如果发现你的代码中结构体是通过—>
访问的话,那么这个结构体变量一定是指针类型的变量。同理如果代码中结构体是通过.
访问的话,那么这个结构体变量就不是指针变量,而是一般的变量。
![](https://i-blog.csdnimg.cn/blog_migrate/5d4cc03ad51e5b67b302cbdbfe0cb16a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c8df0b33a298916e5b1a2c876c3c89e6.png)
总结:到这里结构体在单片机中的应用你已经掌握的差不多了,大家可能感觉本期讲的内容太简单了,不过只有你把这个简单的基础性知识打牢,你就会进步的更快。否则你总感觉你的代码差点意思。