gcc 的字节对齐扩展
一、在阅读ffmpeg代码时经常看到类似下面的语句:
#pragma pack(n)
#pragma pack()
和
__attribute((aligned (8)))
__attribute__ ((packed));
例如:
# ifdef __GNUC__
static inline uint32_t unaligned32(const void *v)
{
struct Unaligned
{
uint32_t i;
} __attribute__((packed));
return ((const struct Unaligned *) v)->i;
}
struct FieldTest
{
int a;
char b;
char c;
}__attribute__((packed)) ft;
// 取消编译器优化对齐,取实际长度
//----------------------------------------------------
这两种方式是gcc的c扩展,用来对齐的,包括栈对齐,变量地址对齐,内存分配对齐和结构体对齐方式.
Note:在设置新的#pragma pack(n)之前,应该先取消以前的对齐,也就是说要调用一下#pragma pack().
#pragma pack(n) //n的取值可以为1、2、4、8,在编译过程中按照n个字结对齐
#pragma pack() //取消对齐,按照编译器的优化对齐方式对齐
__attribute__ ((packed)); //是说取消结构在编译过程中的优化对齐。
__attribute__ ((aligned (n))); //让所作用的成员对齐在n字节自然边界上,如果结构中有成员的长度大于n,则按照机器字长来对齐
gcc手册中说了不建议使用 #pragma,而且#pragma最多只能支持8字节的对齐.
由于不同编译器可能对对齐的实现有区别,在不同编译器编译的程序间进行数据传递就有可能要用到这些东西了.
struct abc {
u32 sid;
u8 p;
union {
int digest[4];
char ctldta[10];
} data;
char dummy[0] __attribute__ ((aligned (64)));
} __attribute__ ((packed));
//__attribute__ ((packed)); 是说取消结构在编译过程中的优化对齐。
//__attribute__ ((aligned (64))); 是说让dummy成员对齐在64-bit自然边界上。
#include
struct FieldTest
{
int a;
char b;
char c;
} __attribute__((aligned(16))) ft; //Aligned by 128 bit
int main(int argc,char** argv)
{
cout<
return 0;
}
//---------------------------------------------------------
g++ packed_test.cpp -o test.exe -Wno-deprecated
./test.exe
the size of FieldTest is 16
人为对齐就是保证每组数据都是4字节的。
例如:
{
char s1[3] ;
long l ;
char s2 ;
}
应改为
{
char s1[3] ;
char s2 ;
long l ;
}
同理,
{
short a ;
long l ;
short b ;
}
应改为
{
short a ;
short b ;
long l ;
}
就是保证每个数据的起始位置符合4字节对齐要求。
若是有奇数个short, 如:
{
short s1;
short s2;
short s3;
long l ;
}
那么要想对齐, 就得手工补足:
{
short s1;
short s2;
short s3;
short temp ;
long l ;
}
这样就不用编译器帮你对齐了。它的最大好处其实是不论1,2,4字节对齐,
编译器生成的结构都是一样的。 这在通信和文件保存方面有莫大的好处。
char也一样, 全应补足为4字节。
二、更改C编译器的缺省字节对齐方式
在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:
1). 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。
2) .使用伪指令#pragma pack (),取消自定义字节对齐方式。
另外,还有如下的一种方式:
1). __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。
2). __attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。
以上的n = 1, 2, 4, 8, 16... 第一种方式较为常见。
三、应用实例
在网络协议编程中,经常会处理不同协议的数据报文。一种方法是通过指针偏移的方法来得到各种信息,但这样做不仅编程复杂,而且一旦协议有变化,程序修改起来也比较麻烦。在了解了编译器对结构空间的分配原则之后,我们完全可以利用这一特性定义自己的协议结构,通过访问结构的成员来获取各种信息。这样做,不仅简化了编程,而且即使协议发生变化,我们也只需修改协议结构的定义即可,其它程序无需修改,省时省力。下面以TCP协议首部为例,说明如何定义协议结构。其协议结构定义如下:
#pragma pack(1) // 按照1字节方式进行对齐
struct TCPHEADER
{
short SrcPort; // 16位源端口号
short DstPort; // 16位目的端口号
int SerialNo; // 32位序列号
int AckNo; // 32位确认号
unsigned char HaderLen : 4; // 4位首部长度
unsigned char Reserved1 : 4; // 保留6位中的4位
unsigned char Reserved2 : 2; // 保留6位中的2位
unsigned char URG : 1;
unsigned char ACK : 1;
unsigned char PSH : 1;
unsigned char RST : 1;
unsigned char SYN : 1;
unsigned char FIN : 1;
short WindowSize; // 16位窗口大小
short TcpCh