嵌入式开发C语言位结构体用途详解

嵌入式开发中,经常需要表示各种系统状态,位结构体的出现大大方便了我们,尤其是在进行一些硬件层操作和数据通信时。但是在使用位结构体的过程中,是否深入思考一下它的相关属性?是否真正用到它的便利性,来提高系统效率?

1.位结构体类型设计

[cpp] view plain copy print?

  1. //data structure except for number structure
  2. typedef struct symbol_struct
  3. {
  4. uint_32 SYMBOL_TYPE :5; //data type,have the affect on "data display type"
  5. uint_32 reserved_1 :4;
  6. uint_32 SYMBOL_NUMBER :7; //effective data number in one element
  7. uint_32 SYMBOL_ACTIVE :1;//symbol active status
  8. uint_32 SYMBOL_INDEX :8; //data index in norflash,result is related to "xxx_BASE_ADDR"
  9. uint_32 reserved_2 :8;
  10. }SYMBOL_STRUCT,_PTR_ SYMBOL_STRUCT_PTR;

分析:这里定义了一个位结构体类型SYMBOL_STRUCT,那么用该类型定义的变量都哪些属性呢?

看下面运行结果:

WORDS是定义的另一个外层类型定义封装,可以把它当作变量来看待。WORDS变量里前5个数据域的地址都是0x1ffff082c,而reserved_2的地址0x1fff0830,紧接着的PressureState变量是0x1fff0834。

开始以为:reserved_1和SYMBOL_TYPE不在一个地址上,因为他们5+4共9位,超过了1个字节地址,但实际他们共用首地址了;而且reserved_2只定义了8位,竟然实际占用了4个字节(0x1fff0834 - 0x1fff0830),我本来是想让他占用1个字节的。WORDS整体占了8个字节(0x1fff0834 - 0x1fff082c),设计时分析占用5个字节

(SYMBOL_TYPE 1个;reserved_1 1个;SYMBOL_NUMBER+SYMBOL_ACTIVE 1个;SYMBOL_INDEX 1个;reserved_2 1个)。

uint_32 reserved_2 : 8; 占用4个字节,估计是uint_32在起作用,而这里写的8位,只是我使用的有效位数,另外24位空闲,如果在下面再定义一个uint_32 reserved_3 : 8,地址也是一样的,都是以uint_32为单位取地址。

同理,上面的5个变量,共用一个地址就不足为奇了。而且有效位的分配不是连续进行的,例如SYMBOL_TYPE+reserved_1 共9位,超过了一个字节,索性系统就分配两个字节给他们,每人一个;SYMBOL_NUMBER+SYMBOL_ACTIVE 共8位,一个字节就能搞定。

2、修改数据结构,验证上述猜想

[cpp] view plain copy print?

  1. //data structure except for number structure
  2. typedef struct symbol_struct
  3. {
  4. uint_8 SYMBOL_TYPE :5; //data type,have the affect on "data display type"
  5. uint_8 reserved_1 :4;
  6. uint_8 SYMBOL_NUMBER :7; //effective data number in one element
  7. uint_8 SYMBOL_ACTIVE :1; //symbol active status
  8. uint_8 SYMBOL_INDEX :8; //data index in norflash,result is related to "xxx_BASE_ADDR"
  9. uint_8 reserved_2 :8;
  10. }SYMBOL_STRUCT,_PTR_ SYMBOL_STRUCT_PTR;

地址数据如下:

当换成uint_8后,可以看到地址空间占用大大减小,reserved_2只占用1个字节(0x1fff069f - 0x1fff069e),其他变量也都符合上面的结论猜想。但是,注意看上面黄色和红色的语句,总感觉有些勉强,那么我又会想,前两个变量数据域是9位,那么他们实际上是不是真正的独立呢?虽然在uint_8上面他们是不同的地址,在uint_32的时候是不是也是不同的地址空间呢?

3、分析结构体内部的数据域是否连续,看下图及结果

本来假设: 由前2次试验的结论,一共占用8个字节,节空间占用:(2+4)+(4+4)+(2+2+4)+(2+2)+(6)。可是,实际效果并不是想的那样。实际只占用了4个字节,系统并没有按照预想的方式,为RESERVED变量分配4个字节。

分析:

这些数据域,整体相加一共32位,占用4个字节(不考虑数据对齐问题)。而实际确实是占用了4个字节,唯一的原因就是:这些数据域以紧凑的方式链接,没有任何空闲位。实际是不是这样呢?

看下图和结果:

这里为了验证是否紧凑链接,用到了一个union数据,后面会讲到用union不会对数据组织方式有任何影响,看实际与上次的一样,也能分析出来。

主要是分析第2和第3个数据域是否紧密链接的。OBJECT_ACTIVE_PRE赋值0b00001111,NUMBER_ACTIVE赋值0b00000101,其他变量都是0,看到WORD数值0b1011111000000。分析WORD数据,可以看到这款MCU还是小端格式(高位数据在高端,低位数据在低端,这里不对大小端进行讨论),断开数据变成(0)10111 11000000,正好是0101+1111,OBJECT_ACTIVE_PRE数据域,跨越了两个字节,并不是刚开始设想的那样。这就印证了上面的紧密链接的结论,也符合数据结果输出。

4、再次实验,分析数据是否紧密链接,看下图和结果

可以看到,RESERVED数据域已经不再属于4个地址空间内了(0x1fff0518 - 0x1fff051b),但是他们整体加起来还是32个位域。这说明数据中间肯定有“空隙”存在了,空隙在哪?看一下NUMBER_STATE,如果紧密的话它应该跟NUMBER_ACTIVE在同一个字节地址上,可是他们并不在一块,“空隙”就存在这里。

这两个结构体有什么不一样?数据类型不一致,一个是uint_32,一个是uint_8。综上所述:数据类型影响的是编译器在分配物理空间时的大小单位,uint_32是以4个字节为单位,而后面的位域则是指在已经分配好的物理空间内部再紧凑的方式分配数据位,当物理空间不能满足位域时,那么系统就再次以一定大小单位进行物理空间分配,这个单位就是上面提到的uint_8或者uint_32。

举例:上面uint_32时,这些位域不管是不是在一个字节地址上,如果能够紧凑的分配在一个4字节空间大小上,就直接紧凑分配。如果不能则继续分配(总空间超过4字节),则再次以4字节空间分配,并把新的位域建立在新的地址空间上(条目1上的就是)。当uint_8时,很明显如果位域不能紧凑的放在一个字节空间上,那么就从新分配新的1字节空间大小,道理是一样的。

5、结构体组合、共用体组合是否影响上述结论

可以看到,系统并没有因为位结构体上面有uint_4的4字节变量或者共用体类型,就改变分配策略把位域都挤到4字节之内,看来他们是没有什么实质性联系的。这里把uint_32改成uint_8,或者把位结构体也替换掉,经我试验证明,都是没有任何影响的。

嵌入式开发服务商朗锐智科www.lrist.com总结:

1、在操作位结构体时,要关注变量的位域是否在一个变量类型(uint_32或者uint_8)上,判断占用空间大小

2、除了位域,还要关注变量定义类型,因为编译器空间分配始终是按类型分配的,位域只是指出了有效位(小于类型占用空间),而且如果位域大于类型空间,编译器直接报错(如 uint_8 test :15,可自行实验)。

3、这两个因素都影响变量占用空间大小,具体可以结合调试窗口,通过地址分配分析判断

4、最重要的一点:上面的所有结果,都是基于我自己的CodeWarrior10.2和MQX3.8分析出来的,不同的编译环境和操作系统,都可能会有不同的结果;而且即便是环境相同,编译器的配置和优化选项都有可能影响系统处理结果。结论并不重要,主要想告诉大家这一块隐藏陷阱,在以后处理类似问题时,要注意分析避让并掌握方法。

时间: 2024-08-29 16:13:01

嵌入式开发C语言位结构体用途详解的相关文章

c语言中结构体对齐详解

为什么要对齐?     现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐.     对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同.一些平台对某些特定类型的数据只能从某些特定地址开始存取.比如有些架构的CPU在访问 一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保

C语言 结构体数组详解及示例代码_C 语言

所谓结构体数组,是指数组中的每个元素都是一个结构体.在实际应用中,结构体数组常被用来表示一个拥有相同数据结构的群体,比如一个班的学生.一个车间的职工等. 定义结构体数组和定义结构体变量的方式类似,请看下面的例子: struct stu{ char *name; //姓名 int num; //学号 int age; //年龄 char group; //所在小组 float score; //成绩 }class[5]; 表示一个班级有5个学生. 结构体数组在定义的同时也可以初始化,例如: str

c语言-一道C语言用结构体排序的题目 大神快来帮我理解下人家的代码~~~

问题描述 一道C语言用结构体排序的题目 大神快来帮我理解下人家的代码~~~ 描述 小王是公司的仓库管理员,一天,他接到了这样一个任务:从仓库中找出一根钢管.这听起来不算什么,但是这根钢管的要求可真是让他犯难了,要求如下: 1. 这根钢管一定要是仓库中最长的: 2. 这根钢管一定要是最长的钢管中最细的: 3. 这根钢管一定要是符合前两条的钢管中编码最大的(每根钢管都有一个互不相同的编码,越大表示生产日期越近). 相关的资料到是有,可是,手工从几百份钢管材料中选出符合要求的那根-- 要不,还是请你编

C#调用C语言结DLL 结构体不会封装,怎么在C#中对应C语言的结构体呢

问题描述 //决策树节点typedefstruct{char*RecordData[1000][100];//训练数据intRecord_Number;/*训练数据的行数*/intAttribute_Number;/*训练数据的列数*/}TrainDataNode;typedefstructDecisionTreeNode{intkeynum;charSelectedAttributeName[30];//选择的属性名charAttributeValue[30];//属性值intRecord_M

详解C语言的结构体中成员变量偏移问题_C 语言

c语言中关于结构体的位置偏移原则简单,但经常忘记,做点笔记以是个记忆的好办法 原则有三个: a.结构体中的所有成员其首地址偏移量必须为器数据类型长度的整数被,其中第一个成员的首地址偏移量为0, 例如,若第二个成员类型为int,则其首地址偏移量必须为4的倍数,否则就要"首部填充":以此类推 b.结构体所占的总字节数即sizeof()函数返回的值必须是最大成员的长度的整数倍,否则要进行"末尾填充": c.若结构体A将结构体B作为其成员,则结构体B存储的首地址的偏移量必须

解析C语言中结构体struct的对齐问题_C 语言

首先看一下结构体对齐的三个概念值: 数据类型的默认对齐值(自身对齐): 1.基本数据类型:为指定平台上基本类型的长度.如在32位机器中,char对齐值为1,short为2,int,float为4,double为8: 结构体:其数据成员中默认对齐值最大的那个值. 2.指定对齐值:#pragma pack (value)时的指定对齐值value. 3.数据类型的有效对齐值:默认对齐值和指定对齐值中小的那个值. 有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式.有效对齐值N是最

详解C语言中结构体的自引用和相互引用_C 语言

结构体的自引用(self reference),就是在结构体内部,包含指向自身类型结构体的指针. 结构体的相互引用(mutual reference),就是说在多个结构体中,都包含指向其他结构体的指针.1. 自引用 结构体 1.1 不使用typedef时错误的方式: struct tag_1{ struct tag_1 A; /* 结构体 */ int value; };         这种声明是错误的,因为这种声明实际上是一个无限循环,成员b是一个结构体,b的内部还会有成员是结构体,依次下去

c语言,对结构体中的整型数组进行赋值.....

问题描述 c语言,对结构体中的整型数组进行赋值..... c语言中,在结构体里声明整型数组,想对整型数组赋值,只能用循环吗?如果我想这样呢..... typedef struct Data { int arr[10]; }Data; int main() { Data data; data = (Data)malloc(sizeof(Data)); data->arr[10]={1,3,2,4,5,6,7,8,9,0}; . ... ... ... } 这样可以吗? 解决方案 绝对不可以.数组作

struct-C语言中结构体中的数组,不能直接赋值吗

问题描述 C语言中结构体中的数组,不能直接赋值吗 设有定义:struct{char mark[12];intnum1;double num2;}t1,t2;若变量均已正确赋初值,则以下语句中错误的是(C) (A) t1=t2; (B) t2.num1=t1.num1; (C) t2.mark=t1.mark;//mark为结构体中的数组,不能直接赋值?? (D) t2.num2=t1.num2; ?====如题参考答案说为结构体中的数组,不能直接赋值,为什么呢?那应该怎么赋值呢?求大侠指教 解决