LinuxCC++编程——常⽤关键字pragmapack和attribute_packed总结
(⼀)pragma pack使⽤说明
⽤sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各⾃占的空间相加,这⾥涉及到内存字节对齐的问题,有时候为了内存对齐需要补齐空字节。通常写程序的时候,不需要考虑对齐问题。编译器会替我们选择适合⽬标平台的对齐策略。当然,我们也可以通知给编译器传递预编译指令⽽改变对指定数据的对齐⽅法。
语法:#pragma pack( [show] | [push | pop] [, identifier], n )
#pragma pack() 能够取消⾃定义的对齐⽅式,恢复默认对齐。
怀孕可以用香水吗作⽤:指定结构,联合和类的包对齐⽅式(pack alignment)
例⼦如下:
(说明,64位GCC,默认8字节对齐)
//这个就规定了4字节对齐
#pragma pack(4)
struct C {
double d;
char b;
int a;
};
//默认8字节对齐
#pragma pack()
struct B {
double d;//8
char b;//1(8)
int a; //4
};
C:8+4+4=16
B:8+8=16
#pragma pack(push)
#pragma pack(4)
struct ......
#pragma pack(pop)
总结:
(1)这样在push和pop之间的结构体就可以按照pack指定的字节(这⾥是4字节对齐⽅式),⽽pop之后的结构体按照#pragma pack(push) 前对齐⽅式。(2)#pragma pack() 取消⾃定义对齐⽅式,恢复默认⽅式,⽽push之后pop是回到push指令之前的对齐⽅式。
(3)#pragma pack(push):英⽂单词push是“压⼊”的意思。编译器编译到此处时将保存对齐状态(保存的是 push指令之前的对齐状态)。
\#pragma pack(pop):英⽂单词pop是”弹出“的意思。编译器编译到此处时将恢复push指令前保存的对齐状态(请在使⽤该预处理命令之前使⽤#pragma pack( push))。
<u>**push和pop是⼀对应该同时出现的名词,只有pop没有push不起作⽤,只有push没有pop可以保持之前对齐状态(但是这样就没有使⽤push的必要了)</u >**
(⼆)_attribute_详解
1. attribute ((packed)) 的作⽤就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占⽤字节数进⾏对齐,是GCC特有的语法。
这个功能是跟操作系统没关系,跟编译器有关,gcc编译器不是紧凑模式的,我在windows下,⽤vc的编译器也不是紧凑的,⽤tc的编译器就是紧凑的。例如:
在Turbo C下:struct my{ char ch; int a;} sizeof(int)=2;sizeof(my)=3;(紧凑模式)
在GCC下:struct my{ char ch; int a;} sizeof(int)=4;sizeof(my)=8;(⾮紧凑模式)
在GCC下:struct my{ char ch; int a;}attrubte ((packed)) sizeof(int)=4;sizeof(my)=5
1. attribute__关键字主要是⽤来在函数或数据声明中设置其属性。给函数赋给属性的主要⽬的在于让编译器进⾏优化。函数声明中的
__attribute((noreturn)),就是告诉编译器这个函数不会返回给调⽤者,以便编译器在优化时去掉不必要的函数返回代码。
GNU C的⼀⼤特⾊就是__attribute__机制。__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。
__attribute__书写特征是:__attribute__前后都有两个下划线,并且后⾯会紧跟⼀对括弧,括弧⾥⾯是相应的__attribute__参数。
__attribute__语法格式为:
attribute ((attribute-list))
其位置约束:放于声明的尾部“;”之前。
函数属性(Function Attribute):函数属性可以帮助开发者把⼀些特性添加到函数声明中,从⽽可以使编译器在错误检查⽅⾯的功能更强⼤。__attribute__机制也很容易同⾮GNU应⽤程序做到兼容之功效。
GNU CC需要使⽤ –Wall编译器来激活该功能,这是控制警告信息的⼀个很好的⽅式。
packed属性:使⽤该属性可以使得变量或者结构体成员使⽤最⼩的对齐⽅式,即对变量是⼀字节对齐,对域(field)是位对齐。
如果你看过GPSR协议在TinyOS中的实现,你⼀定会注意到下⾯的语句:
typedef struct {
double x;
double y;
} __attribute__((packed)) position_t;
GNU C的⼀⼤特⾊(却不被初学者所知)就是__attribute__机制。__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。
__attribute__语法格式为:
attribute ((attribute-list))
其位置约束为:放于声明的尾部“;”之前。
packed是类型属性(Type Attribute)的⼀个参数,使⽤packed可以减⼩对象占⽤的空间。需要注意的是,attribute属性的效⼒与你的连接器也有关,如果你的连接器最⼤只⽀持16字节对齐,那么你此时定义32字节对齐也是⽆济于事的。
使⽤该属性对struct或者union类型进⾏定义,设定其类型的**每⼀个变量的内存约束**。当⽤在enum类型定义时,暗⽰了应该使⽤最⼩完整的类型(it indicates that the smallest integral type should be ud)。
下⾯的例⼦中,my-packed-struct类型的变量数组中的值会紧凑在⼀起,但内部的成员变量s不会被“pack”,如果希望内部的成员变量也被packed的话,my-unpacked-struct也需要使⽤packed进⾏相应的约束。
struct my_unpacked_struct
{
char c;
int i;
};
struct my_packed_struct
{
char c;
int i;
struct my_unpacked_struct s;
}__attribute__ ((__packed__));
总结:结构体后⾯加上attribute ((packed))即是不⽤字节对齐。
在每个系统上看下这个结构体的长度吧。
内存对齐,往往是由编译器来做的,如果你使⽤的是gcc,可以在定义变量时,添加__attribute__,来决定是否使⽤内存对齐,或是内存对齐到⼏个字节,以上⾯的结构体为例:
1)attribute ((aligned(4))) 对齐到4字节,同样可指定对齐到8字节,设置_attribute__ ((aligned(8)))。
{
char name[7];
uint32_t id;
char subject[5];花呗逾期会怎么样
} __attribute__ ((aligned(4)));
小孩痱子
2)attribute_ ((packed)) 不对齐,结构体的长度,就是各个变量长度的和
struct student
{
建议用英语怎么说char name[7];
uint32_t id;
char subject[5];
} __attribute__ ((packed));
补充:跨平台时基于数据结构的⽹络通信
⽹络通信通常分为基于数据结构的和基于流的。HTTP协议就是后者的⼀个例⼦。
有时为了提⾼程序的处理速度和数据处理的⽅便,会使⽤基于数据结构的通信(不需要对流进⾏解析)。但是,当需要在多平台间进⾏通信时,基于数据结构的通信,往往要⼗分注意以下⼏个⽅⾯:
[1] 字节序
[2] 变量长度
[3] 内存对齐
在常见的系统架构中(Linux X86,Windows),⾮单字节长度的变量类型,都是低字节在前,⽽在某些特定系统中,如Soalris Sparc平台,⾼字节在前。如果在发送数据前不进⾏处理,那么由Linux X86
发向Soalris Sparc平台的数据值,势必会有极⼤的偏差,进⽽程序运⾏过程中⽆法出现预计的正常结果,更严重时,会导致段错误。
对于此种情况,我们往往使⽤同⼀的字节序。在系统中,有ntohXXX(), htonXXX()等函数,负责将数据在⽹络字节序和本地字节序之间转换。虽然每种系统的本地字节序不同,但是对于所有系统来说,⽹络字节序是固定的-----⾼字节在前。所以,可以以⽹络字节序为通信的标准,发送前,数据都转换为⽹络字节序。
转换的过程,也建议使⽤ntohXXX(), htonXXX()等标准函数,这样代码可以轻松地在各平台间进⾏移植(像通信这种很少依赖系统API的代码,做成通⽤版本是不错的选择)。
变量的长度,在不同的系统之间会有差别,如同是Linux2.6.18的平台,在64位系统中,指针的长度为8个字节,⽽在32位系统中,指针⼜是4个字节的长度—此处只是举个例⼦,很少有⼈会将指针作为数据发送出去。下⾯是我整理的在64位Linux系统和32位Linux系统中,⼏种常见C语⾔变量的长度:
short int long long long ptr time_t
32位 2 4 4 8 4 4闪光对焊
64位 2 4 8 8 8 8
送向日葵在定义通信⽤的结构体时,应该考虑使⽤定常的数据类型,如uint32_t,4字节的固定长度,并且这属于标准C库(C99),在各系统中都可使⽤。
**内存对齐的问题,也与系统是64位还是32位有关。**如果你⼿头有32位和64位系统,不妨写个简单的程序测试⼀下,你就会看到同⼀个结构体,即便使⽤了定常的数据类型,在不同系统中的⼤⼩是不同的。对齐往往是以4字节或8字节为准的,只要你写的测试程序,变量所占空间没有对齐到4或8的倍数即可,举个简单的测试⽤的结构体的例⼦吧:
struct student
{
char name[7];
uint32_t id;
char subject[5];
};
在每个系统上看下这个结构体的长度吧。
内存对齐,往往是由编译器来做的,如果你使⽤的是gcc,可以在定义变量时,添加__attribute__,来决定是否使⽤内存对齐,或是内存对齐到⼏个字节,以上⾯的结构体为例:
1)到4字节,同样可指定对齐到8字节。
struct student
{
char name[7];
uint32_t id;
char subject[5];
} attribute ((aligned(4)));
2)不对齐,结构体的长度,就是各个变量长度的和struct student
职业规划{
射精痛char name[7];
uint32_t id;
char subject[5];
} attribute ((packed));