[技术问答] 单片机的结构体

[复制链接]
837|2
 楼主| uiint 发表于 2025-4-13 22:49 | 显示全部楼层 |阅读模式
在C语言和单片机之间需要一个过渡!这个需要过渡的点在很多单片机视频教程中并没有去讲解。因为教育机构默认你是知道的,所以在讲流水灯时他们并不会讲解GPIO初始化这个结构体,因为默认你是知道如何操作的。



申明一个GPIO_InitTypeDef的结构体,然后在LED_Init(void)函数中定义一个GPIO_InitStructure的变量GPIO_InitStructure,那么这个变量就可以设置这个GPIO_InitTypeDef的结构体中的成员。这里先做了解,请接着往下看。
1、为什么需要结构体这里先不说什么是结构体,说说为什么需要结构体?只有知道为什么需要,才能按照你的需要去学习,这样效率才会高。你才知道在什么情况下我们需要写一个结构体,怎么样去用结构体。
这里我们以一个智能家居的项目为例。
先来看一个实际的问题
话说有一个项目上有4个传感器:光照传感器、烟雾传感器、酒精传感器、湿度传感器。然后这四个各个传感器还有设置报警的阈值范围。
一般都是这样写
  1. #include "sys.h"
  2. #include "delay.h"
  3. #include "usart.h"

  4. /*记录传感器的数值*/
  5. float temperature; //温度
  6. char  humidity;  //湿度
  7. char  alcohol;  //酒精浓度
  8. int   illumination; //光照强度

  9. /*记录传感器高低阈值*/
  10. float temperature_threshold[2];
  11. float humidity_threshold[2];
  12. float alcohol_threshold[2];
  13. float illumination_threshold[2];

  14. int main(void)
  15. {
  16. uart_init(115200);//串口初始化
  17. delay_init();
  18.     while(1)
  19. {
  20. }
  21. }








当然你做一个项目肯定还定义了很多其他的变量,还需要记录其它变量



然后过了几天又增加了个一氧化碳传感器



然后过了几天,每个传感器还需要加个是否正常工作的标志位



因为项目的需要,然后又增加了4个相同的传感器:温湿度、光照强度、烟雾浓度、酒精浓度。



然后又增加了4个相同的传感器:温湿度、光照强度、烟雾浓度、酒精浓度。
截图截不开了....




在项目刚开始做的时候如果不能未雨绸缪,接着干下去整个程序代码别说维护了,就是接着写都让人头疼!

2、结构体闪亮登场然后搞C语言那帮家伙就造了个功能struct
1、结构体就是可以把变量包含到里面的东西
struct就代表要定义一个结构体,sensors是这个结构体的名字, 然后是一个大括号 { }
大括号里面就随意定义变量啦~



怎么使用里面的变量呢?
注意结构体是一个数据类型就像是int和char一样的这种类型
既然是一种数据类型, 那么就可以用这个数据类型定义变量
定义一个该结构体的变量



为啥要那样子定义啊?
答:你去问造C语言的那帮家伙去!问问他们为啥要设计成这样子!
然后操作结构体变量里面的成员变量。当我们定义好结构体变量后,在初始化变量里面的成员变量时就会自动出现结构体里面的成员变量,如果这个代码是你一个一个敲出来的话,你就会感叹结构体在单片机中是那么的奇妙!









有人会问为啥是结构体变量中间加个点?
答:你去问造C语言的那帮家伙去!问问他们为啥要设计成这样子。
2、其实定义结构体变量可以下面这样子



也可以定义多个






发现了没,每个结构体变量都是单独拥有结构体里面的全部成员变量。
就像是最开始说的,如果再增加一套传感器:温湿度、光照强度、烟雾浓度、酒精浓度。
使用结构体的话只需要再定义一个结构体变量即可。
但是很多时候我们在单片机中见到的结构体并不是上面那样定义的,而是在前面加了一个typedef 关键字。
这样的例子在库函数的头文件中我们经常会看到如下结构体



3、typedef关键字先看一下百度百科对typedef的定义



总结一句就是:typedef可以把一个数据类型取一个别的名字
typedef {数据类型}  {别的名字}
  1. #include "sys.h"
  2. #include "delay.h"
  3. #include "usart.h"

  4. typedef int zhjiguoxin;//zhjiguoxin就是int

  5. zhjiguoxin value = 0;

  6. int main(void)
  7. {
  8. uart_init(115200);//串口初始化
  9. delay_init();

  10. printf("value=%d\r\n",value);

  11.     while(1)
  12. {
  13. }
  14. }











虽然typedef可以给变量取别名,但是没有谁会像上面那样取名字,我这里只是举一个例子。
4、结构体的精髓注意下:
1、下面的代表了这个结构体数据类型



2、给这个数据类型起一个别名
注意是三部分,  typedef  {数据类型}  {别的名字}。所以sensor就代表了这个结构体了。
建议初学者把下面这张图保存到你的电脑,这样你就***也不会忘记typedef在结构体中的用法了,也能很快的记住结构体这个东东。



3、以后定义结构体变量的时候就不需要像最开始那样struct sensors sen;这样的定义结构体变量了,只需要sensor sen;即可。

4、结构体名字可以省略
注意结构体定义可以不写结构体名,对C语言来说,那个sensors不叫结构体名,而是叫标签(tag)。C语言结构体名是struct关键字 + tag。所以为了简便我们看到的单片机中的结构体都是写成如下的形式。
5、结构体的变量可以放任何变量1、结构体变量可以放任何变量(int型指针)
  1. #include "sys.h"
  2. #include "delay.h"
  3. #include "usart.h"
  4. typedef struct
  5. {
  6. float temperature; //温度
  7. char  humidity;    //湿度
  8. char  alcohol;    //酒精浓度
  9. int   illumination;//光照强度
  10. char  CO;  //一氧化碳浓度
  11. int   *p;  //int型的指针变量
  12. } sensor;
  13. sensor sen;
  14. int value =0;
  15. int main(void)
  16. {
  17. uart_init(115200);//串口初始化
  18. delay_init();
  19. sen.p=&value;//把value的地址赋值
  20. //打印p代表的地址里面的值(其实就是打印value的值)
  21. printf("value=%d\r\n",*(sen.p));
  22.     while(1)
  23. {
  24. }
  25. }





既然是指针变量,所以给指针变量赋值时当然是赋值的是一个地址。




2、结构体变量可以放任何变量(函数指针)
  1. #include "sys.h"
  2. #include "delay.h"
  3. #include "usart.h"
  4. typedef struct
  5. {
  6. float temperature; //温度
  7. char  humidity;    //湿度
  8. char  alcohol;    //酒精浓度
  9. int   illumination;//光照强度
  10. char  CO;  //一氧化碳浓度
  11. int   *p;  //int型的指针变量
  12. void (*fun)();
  13. } sensor;
  14. sensor sen;
  15. void function()
  16. {
  17. printf("zhiguoxin\r\n");
  18. }
  19. int value =0;
  20. int main(void)
  21. {
  22. uart_init(115200);//串口初始化
  23. delay_init();
  24. sen.fun=function;
  25. sen.fun();
  26.     while(1)
  27. {
  28. }
  29. }





既然是函数指针变量,所以给函数指针变量赋值时当然是赋值的也是地址,并且还要是一个函数的地址,而一个函数的函数名就是该函数的地址。所以才会有下面的把函数function();的地址function赋值给函数指针fun。这样大家是不是很清楚了。如果不清楚建议看个3遍以上!




3、结构体变量可以放任何变量(结构体变量)
这就是结构体嵌套,在一个结构体内包含了另一个结构体作为其成员。当出现结构体嵌套时,必须以级联方式访问结构体成员,即通过成员选择运算符逐级找到最底层的成员时再引用。
  1. #include "sys.h"
  2. #include "delay.h"
  3. #include "usart.h"

  4. typedef struct
  5. {
  6.   int i;
  7. }zhiguoxin;

  8. typedef struct
  9. {
  10. float temperature; //温度
  11. char  humidity;    //湿度
  12. char  alcohol;    //酒精浓度
  13. int   illumination;//光照强度
  14. char  CO;  //一氧化碳浓度
  15. int   *p;  //int型的指针变量
  16. void (*fun)();
  17. zhiguoxin guougo;
  18. }sensor;

  19. sensor sen;

  20. int main(void)
  21. {
  22. uart_init(115200);//串口初始化
  23. delay_init();

  24. sen.guougo.i=100;
  25. printf("i=%d\r\n",sen.guougo.i);
  26.   
  27.     while(1)
  28. {
  29. }
  30. }











4、结构体变量可以放任何变量(结构体指针)
结构体是一个数据类型。数据类型当然也可以定义对应的指针变量啦。
就像是int 类型可以定义 int *p; 一样



所以当大家如果发现你的代码中结构体是通过—>访问的话,那么这个结构体变量一定是指针类型的变量。同理如果代码中结构体是通过.访问的话,那么这个结构体变量就不是指针变量,而是一般的变量。







xixi2017 发表于 2025-4-15 15:49 | 显示全部楼层
结构体的优势就是可以将函数和变量封装到一起,使用函数指针,非常神奇。
少女诗篇 发表于 2025-8-28 12:08 | 显示全部楼层
单片机的结构体是自定义数据类型,可将不同类型数据(如 GPIO 引脚号、寄存器地址、状态标志)打包成整体,比如定义 “电机控制结构体” 包含转速、方向、使能位。能简化代码,让数据管理更清晰,方便函数传参(无需多个零散参数),还可结合指针实现复杂数据操作,提升程序模块化与可维护性,广泛用于外设配置、数据采集等场景。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

59

主题

4558

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部