结构体、联合体的应用,以及字节对齐和字节序的问题

来源:互联网 发布:分水岭算法流程图 编辑:程序博客网 时间:2024/06/06 00:47

通讯中,用过的非常多。给你一个我的例子


typedef union
{
    FLOAT32        fVal;
    INT32        dig;
    INT32        iVal;
   
struct{
        FLOAT32 x;
        FLOAT32 y;
    }complex;
    FLOAT64     dfVal;
    PROT_TIME   time;
}PROTEVENT_VALTYPE;   
//不同类型的数值

typedef
struct
{
   
struct
    {
        UINT16    paraType;
        UINT8    valType;
        UINT8   widthVal;
        PROTEVENT_VALTYPE val;
        UINT16    paraGroup;
        UINT8   phs;
        UINT8   lenName;
    }head;
    UINT8    name[VAR_COMM_SIZE];
}PROTMSG_EVENT_PAR;


对于那种一个整数和几个字节取值的问题,建议不要用结构联合来实现。如:

union bits32
{
   
int num;
   
struct
    {
       
char c1;
       
char c2;
       
char c3;
       
char c4;
    }
byte;
};


这个在不同的CPU架构变现不相同,就是CPU的高低次序问题。在普通的PC即x86cpu上,是小头在前,即c1对应的num的第一个字节,但是在其它一些如PowerPC、sun的SPARC架构CPU上,是大头在前,c1就对应num的第4个字节。
在网络传输中,对这个问题很敏感,就是发生的字节次序问题。在TCP/IP中,规定是网络次序,具体参考一下TCP/IP的数据。里面有不少网络次序数据和本机数据的转换问题。
为了避免这种问题,我们一般在通信中,采用的是移位操作。如

/*****整数和单字节数转换操作,通过移位避开次序问题*****/
#define BYTE0(intval) ( (UINT8)(  0x000000ff&(intval)) )
#define BYTE1(intval) ( (UINT8)(( 0x0000ff00&(intval))>>8) )
#define BYTE2(intval) ( (UINT8)(( 0x00ff0000&(intval))>>16) )
#define BYTE3(intval) ( (UINT8)((0x0ff000000&(intval))>>24) )

/*******************字节组合定义*******************/
#define UINT16COMB(b1,b0)    ( (UINT16)(b0) | (((UINT16)(b1))<<8) )
#define INT16COMB(b1,b0)    UINT16COMB(b1,b0)

#define UINT32COMB(b3,b2,b1,b0)            /
                (   (UINT32)(b0)    /
               
| (((UINT32)(b1))<<8)    /
               
| (((UINT32)(b2))<<16)    /
               
| (((UINT32)(b3))<<24) )
#define INT32COMB(b3,b2,b1,b0)    UINT32COMB(b3,b2,b1,b0)

使用时,可以用:

c1
=BYTE0(num);
c2
=BYTE1(num);
...
num
=UINT32COMB(c3,c2,c1,c0);

实际上移位操作,对于CPU来说,基本上是一个指令周期,比你直接用struct和union还要快。这个问题涉及到32位CPU的整字对齐和整字操作处理问题和CPU的指令优化,具体可以去参考CPU指令的书籍。

对齐规则由编译选项决定的,一般编译器采用缺省的编译选项。一般存储单元按照CPU的字长对齐,对于目前32位主流系统,基本的数据类型char、short、int、float,对齐在整字的开始。
如struct
{
   char c;
   int i;
   float f;
};
c占用第一个字长的第一个字节,而后面的3个字节变长存储空洞;i和f各占1个字长。
struct
{
   char c;
   char c1;
   int i;
   float f;
};
这种情况下,c1将分配在c之后,占一个字节,而c1后面就留出来2各空洞。
所以在组织struct或者class时,注意一下成员分布的合理,综合一下存储分配情况。但是从CPU的指令操作上看,CPU一般按照一个字长来执行,操作一个int数要比一个char效率高。也就说说,当CPU读取一个char数时,CPU先从内存中读取一整个字长值到寄存器中,然后将相应char所在的那个字节提取出来,从而完成一个char的取值。
另外:编程时千万注意float *pFVal;指针的使用,浮点数指针一定要指向一个整字起始的内存位置,如上面的&c1地址,否则在一些特定的CPU或单片机中,对非对齐的浮点指针操作,会发生崩溃问题,这个和浮点指令的兼容性问题。典型的是在sun的sparc、POWERPC的一些cpu上就会发生崩溃问题。

 

想改变编译器的对齐规则,可以在代码中加入:

#pragma pack(push, 1)

.....//代码

#pragma pack(pop)