[技术讨论] 【C语言实战经验9】如何更加有效的定义结构体?

[复制链接]
 楼主| dffzh 发表于 2025-7-7 08:49 | 显示全部楼层 |阅读模式
本帖最后由 dffzh 于 2025-7-7 08:51 编辑

#申请原创#
@21小跑堂

结构体是C语言编程里面非常重要的一个数据结构,也属于一种复合数据类型,用关键字struct来表示,是由一批数据组合而成的结构型数据,而每个数据我们可以称之为结构体的成员或者域或者元素。
那什么情况下适合用结构体来封装一些数据呢?如果要表示的数据存在一定的关联性,就可以尝试用结构体来封装。
我们先来举几个简单的例子:
对于某一种水果的相关属性定义,比如颜色、形状、价格及产地等;
对于某种具体的通信协议帧数据内容,比如帧头,帧尾,长度,数据及校验码等;
对于某个设备的一些状态,错误或故障信息,比如超温报警,掉电报警及超阈值报警等;
还有MCU外设相关的定义等等;
反正,只要存在整体-局部关系或者要表示的属性相似的时候,你都可以用结构体来实现。
那怎么样定义结构体比较好呢?
可以按如下方式定义:
struct结构体名称
{
    结构体成员;
}结构体变量1;
如果要再定义结构体变量2,可以按如下方式:
struct 结构体名称 结构体变量2;
上面这种方式在每次新增结构体变量时,都要加上struct关键字声明,略显繁琐,因此我们可以用typedef来简化结构体的定义方式:
typedef struct
{
    结构体成员;
}结构体名;
要定义结构体变量时,按如下方式即可:
结构体名 结构体变量;
可以看看MCU固件库,基本上都是用这种方式来定义结构体的:


USART_InitTypeDef USART_InitStructure;
其中结构体变量可以为普通结构体变量,可以为结构体指针,可以为结构体数组:
结构体名 struct_variable;
结构体名 *pStructVariable;
结构体名 struct_variable[10];
结构体成员可以是普通数据类型,也可以是结构体和联合体等复合数据类型。
有时候对于内存空间资源比较紧张或者想让结构体成员变量指示的更加明显的情况下,在定义结构体时还可以加入位域(bit fields)操作,所谓位域,是C语言中用于优化内存占用的数据结构,通过在结构体或联合体声明中使用冒号语法指定成员的位宽,将多个逻辑信息压缩存储至单个整型单元。
即在成员变量后面加上“:位域大小”来定义该成员变量占用几位bit,如下所示:
  1. typedef struct
  2. {     
  3.         unsigned int is_active : 1;  //只占1位     
  4.         unsigned int age : 7;      //占7位
  5.         unsigned int gender : 1;   //只占1位
  6. } CompactPerson;
这样可以达到有效节约内存空间的目的,也会让成员变量的数据范围变的更加直观。另外,还可以通过结构体对齐方式来优化结构体所占的字节数,我们举例说明吧。先来看看下面这个结构体定义方式所占的字节数:
  1. typedef struct
  2. {
  3.     char c; //1字节
  4.     int i;  //4字节
  5.     char d; //1字节
  6. }Example;
如果按照成员变量所占字节数相加来计算,Example结构体应该是占用6字节的,对吧?但是实际呢?
为什么是占用12字节呢?我们知道,对于32位MCU来说,一个内存地址是存储4字节数据,为了提高程序运行效率,系统是会考虑到内存对齐操作的,即第一个成员变量c只有1字节,不够4字节,那编译器通常会在此处添加3个填充字节,第三个成员变量d也是一样,这样结构体所占字节总数即为12字节,在内存中的存储结构如下:
  c: 1字节  
填充 3字节
  i:  4字节  
  d: 1字节  
填充 3字节
也就是结构体所占字节数肯定是4字节的整数倍(对于32位MCU)。那怎么优化呢?可以尝试按成员变量本身所占字节数大小的降序方式进行排列来定义:
  1. typedef struct
  2. {
  3. int i;  //4字节
  4. char c; //1字节
  5.     char d; //1字节
  6. }Example;
再测试看一下:
结构体所占字节数变成了8字节,较之前的定义方式减少了4字节,是不是很好?在内存中的存储结构如下:
  i:  4字节  
  c: 1字节  
d: 1字节
填充 2字节
如果你使用的结构体比较多或者里面定义的成员比较多,还是有必要做一些结构体定义方式的优化操作的。此外,还可以通过使用联合体来共享内存空间,其实在结构体中嵌套联合体是比较常见的C编码技巧,在MCU外设库里也是应用比较多的,此时联合体的所有成员是共享同一块内存空间的,并且其大小为最大成员的大小,这种操作可以有效节约内存空间,比较适用于同一时刻只需要使用其中某一个成员的场景。以下在结构体Packet里定义了一个联合体成员data,可以根据type决定使用哪个成员:
  1. typedef struct
  2. {     
  3.         int type;  //类型标签     
  4.     union
  5.         {         
  6.                 int intValue;         
  7.                 float floatValue;         
  8.                 char stringValue[6];     
  9.         }data;   
  10. }Packet;
可以按如下方式来使用:
Packet p;p.type = 1;
p.data.floatValue = 3.1415f;  // 此时联合体存储的是float
最后,对成员变量定义合理的数据类型也是非常重要的,不要想当然的从头至尾全是一种数据类型。
以上,通过对结构体的定义方式和实测说明讲解了一下如何更加有效地定义结构体。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
您需要登录后才可以回帖 登录 | 注册

本版积分规则

109

主题

1164

帖子

22

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

109

主题

1164

帖子

22

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