【C语言常识】结构体与联合体

时间:2022-06-01 21:23:41

结构体与联合体是C语言的常见数据类型,可对C的基本数据类型进行组合使之能表示复杂的数据结构,意义深远,是优异代码的必备工具。

一.        struct union的基本用法,在语法上unionstruct相同,因此只以struct为例
1.1       
基本用法:

1. struct AA{ int a; int b;}; //结构体的声明和定义

2. struct AA s1; //变量的声明和定义 

3. s1.a=3; //变量的使用:

1.2       在声明结构体的同时定义变量:

1. struct AA{int a; int b}s2={1, 2}; //此处AA为可选项,初始化部分为可选

2. s2.a=5;                //变量的使用

1.3       使用 typedef来简化struct的类型说明:

1. typedef struct AA{int a; int b;}SAA; 

2. SAA s3, s4;        //变量的声明和定义

3. s3.a=8; //变量的使用

1.4 使用时尤其注意后面的分号,必不可少;
二.        struct union的区别
2.1 struct
中的成员是按顺序依次排列,互相独立,在排列时引入了对齐问题(2.2);而union中的成员共用一块内存,起始地址都是相同的,即union中的成员是对同一地址的多个引用、对同一地址的多种表达方法。
2.2 struct
的对齐问题
对齐问题即struct中每个成员起始地址的分配。为了可以快速访问到每个成员,(以EW430为例)编译器根据成员的类型放到合适的地址上,默认为2字节对齐。如:
Struct AA{char c; int d;}aa;

假设aa被分配在地址N上,并且N为偶数,则aa.c的地址为Naa.d的地址为N+2,而N+1地址未使用被浪费了。
对齐字节大小的指定:通过#pragma pack(n)来指定对齐字节的大小,n为某些常量(EW430中可取1,2,4,8,16),指定以n字节对齐。通常使用编译器默认的对齐大小最为适宜。如果指定对齐大小为1字节对齐,以上面的结构体变量aa为例,aa.c地址为Naa.d地址为N+1,是个奇数地址,因为430在奇数地址只能读取一个字节,因此要访问d成员需要读取两次才能完成,后果是代码变长、速度变慢。
三.       匿名结构体与联合体
匿名结构体和联合体,即没有名字的结构体或者联合体,这种结构体(或联合体)无法通过.->操作符引用(因为它所属的结构体或联合体没有名字,无法应用),而像暴露在外面一样,与外层作用域相同,可直接使用。
3.1 C
标准中提及的匿名结构体和联合体(草稿原文引用)(C标准中提及的匿名结构(联合)体应该只适用于有名结构体(联合)中的匿名结构(联合)体成员)

 

1. struct v{

2.         union{//匿名联合体

3.                struct {int i, j;}; //匿名结构体

4.                struct {long k, l;}w;

5.         };

6.         int m;

7. }v1;

8. v1.i=2; //合法,匿名结构体的成员被直接使用

9. v1.k=3;//非法,有名字的结构体需要引用他的名字,如下

10.v1.w.k=5; //合法

3.2 IAR中的匿名结构体和联合体
IAR
中的匿名结构体和联合体可以具有全局作用域,因此结构体或联合体中的成员可以作为全局变量使用,但却兼具结构体或者联合体的属性。
1        IO430.h系列头文件的寄存器声明方式:
如:

1. __no_init volatile union

2. {

3.   unsigned shortSFRIE1;   /* Interrupt Enable 1 */

4.  

5.   struct

6.   {

7.     unsigned shortWDTIE           : 1; /* WDT InterruptEnable */

8.     unsigned shortOFIE            : 1; /* Osc FaultEnable */

9.     unsigned short               : 1;

10.    unsigned shortVMAIE           : 1; /* Vacant MemoryInterrupt Enable */

11.    unsigned shortNMIIE           : 1; /* NMI InterruptEnable */

12.    unsigned shortACCVIE          : 1; /* Flash Access ViolationInterrupt Enable */

13.    unsigned shortJMBINIE         : 1; /* JTAG Mail Box inputInterrupt Enable */

14.    unsigned shortJMBOUTIE        : 1; /* JTAG Mail Box outputInterrupt Enable */

15.  }SFRIE1_bit;

16.} @0x0100;

说明:[1].  SFRIE1SFRIE1_bit具有全局变量的性质,可以直接被引用。
        [2].        SFRIE1
SFRIE1_bit在同一联合体中,根据联合体的性质,他们共享同一地址,即对同一寄存器不同访问方式,SFRIE1对整个寄存器操作,SFRIE1_bit可对某位操作,如:
SFRIE1 |= 0x0001;
SFRIE1_bit.WDTIE = 1;具有相同效果。
        [3].       
冒号(:)与整数称之为位域,使用位域表达的变量使用同一数据中的不同位,并按顺序排列。整数表示这个变量占用多少位。没有名字的位域不能被引用到,一般用来保留未使用到的位(占位作用)
(2)  在应用程序中也可以使用此特性来实现一些功能。
记得某位网友曾提出过这样的问题,要高效的使用一个整数的高8位和低8位,则可以用如下代码解决:

1. union {

2.         unsignedint num;

3.         struct {

4.                unsigned char nLow;

5.                unsigned char nHigh;

6.         };

7. };

或者

1. union {

2.         unsignedint num;

3.         struct {

4.                unsigned int nLow        :8;

5.                unsigned int nHigh         :8;

6.         };

7. };

8. int a = num;

9. int b = nLow;

10.int c = nHigh;

非常好的解决方案,只占用一个整数的空间,可以对高8位、低8位、整个16位引用而不需要计算。

(3) 联合体中的位域(补充)

1. union {

2.         unsignedint z;

3.         unsignedint z1 : 1;

4.         unsignedint z2 : 2;

5.         unsignedint z3 : 3;

6.         unsignedint z4 : 4;

7. };

8. z = 0xFFFF;

9. a = z1;

10.b = z2;

11.c = z3;

结果:a = 1, b = 3, c = 7;

(4)结构体中的位域长度0(补充)

1. union ZZ{

2.         unsignedint z1 : 1;

3.         unsignedint z2 : 1;

4.         unsignedint z3 : 1;

5.         unsignedint z4 : 1;

6.         unsignedint : 0;

7.         unsignedint z6 : 1;

8. }zz;

长度为0的位域是通知编译器不要在以前的单元上分配位域了,代表着一个单元的位域分配结束,这个长度为0的位域不能有名字。以后的位域分配要新开辟一个单元。