您的位置:首页 > 其它

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

2010-08-22 11:18 225 查看
通讯中,用过的非常多。给你一个我的例子

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)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: