GNU C的一大特色就是__attribute__机制。__attribute__机制可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。
__attribute__语法格式为:__attribute__((attribute-list))。
__attribute__对结构体(struct)或共用体(union)进行属性设置:
大致有六个参数值可以被设定,即:aligned,packed,transparent_union,deprecated和may_alias。
在使用__attribute__参数时,你也可以在参数的前后都加上“__”(两个下划线),例如,使用__aligned__而不是aligned,这样,你就可以在相应的头文件里使用它而不需要关心头文件里是否有重名的宏定义。
1. aligned(alignment):
设定对齐的格式(以字节为单位),例如:
struct S { short b[3]; }__attribute__((aligned (8)));
typedef int int32_t __attribute__((aligned(8)));
/*
* 该声明将强制编译器确保(尽可能)变量类型为structS或者int32_t的变量在分配空间时采用8字节对齐方式。
*/
如上所述,你可以手动指定对齐的格式,同样,你也可以使用默认的对齐方式。如果aligned后面不指定数值,那么编译器将依据你的目标机器情况使用最大最有益的对齐方式。例如:
struct S { short b[3]; } __attribute__((aligned)); // 使用默认对齐方式,依据目标机器,使用最大最有益的对齐方式
#include <stdio.h> struct S1 { short b[3]; }; struct S2 { short b[3]; } __attribute__((aligned(8))); struct S3 { short b[3]; } __attribute__((aligned(16))); struct S4 { short b[3]; } __attribute__((aligned(32))); struct S5 { short b[3]; } __attribute__((aligned(64))); struct S6 { short b[3]; } __attribute__((aligned)); int main(int argc, char** argv) { printf("sizeof(struct S1) = %ld ", sizeof(struct S1)); printf("sizeof(struct S2) = %ld ", sizeof(struct S2)); printf("sizeof(struct S3) = %ld ", sizeof(struct S3)); printf("sizeof(struct S4) = %ld ", sizeof(struct S4)); printf("sizeof(struct S5) = %ld ", sizeof(struct S5)); printf("sizeof(struct S6) = %ld ", sizeof(struct S6)); return 0; }
/*
* 输出结果:
* sizeof(struct S1) = 6
* sizeof(struct S2) = 8
* sizeof(struct S3) = 16
* sizeof(struct S4) = 32
* sizeof(struct S5) = 64
* sizeof(struct S6) = 16
*/
注意:__attribute__属性的效力与你的链接器有关,如果你的链接器最大只支持16字节对齐,那么你此时定义32字节对齐也无济于事。
#include <stdio.h> struct A { int a; char b; short c; } aa; struct AP { int a; char b; short c; } __attribute__((aligned(16))) ap; struct B { char a; int b; short c; } bb; struct BP { char a; int b; short c; } __attribute__((aligned(4))) bp; struct C { int a; char b; struct AP px; short c; } cc; struct CP1 { int a; char b; struct AP px; short c; } __attribute__((aligned(4))) cp1; struct CP2 { int a; char b; struct AP px; short c; } __attribute__((aligned(8))) cp2; int main(int argc, char** argv) { printf("sizeof(aa) = %lu, sizeof(ap) = %lu ", sizeof(aa), sizeof(ap)); printf("sizeof(bb) = %lu, sizeof(bp) = %lu ", sizeof(bb), sizeof(bp)); printf("sizeof(cc) = %lu, sizeof(cp1) = %lu ", sizeof(cc), sizeof(cp1)); printf("sizeof(cc) = %lu, sizeof(cp2) = %lu ", sizeof(cc), sizeof(cp2)); return 0; }
/*
* 输出结果:
* sizeof(aa) = 8, sizeof(ap) = 16
* sizeof(bb) = 12, sizeof(bp) = 12
* sizeof(cc) = 48, sizeof(cp1) = 48
* sizeof(cc) = 48, sizeof(cp2) = 48
*/
关于内存对齐:(其中的#pragma pack()在gcc中很少见,具体用法需确认)
① 什么是内存对齐?
不同类型的数据在内存中按照一定的规则排列;而不是顺序的一个接一个的排放,这就是对齐。
#include <stdio.h> struct Test1 { char c1; short s; char c2; int i; }; struct Test2 { char c1; char c2; short s; int i; }; int main(int argc, char** argv) { printf("sizeof(struct Test1) = %lu ", sizeof(struct Test1)); printf("sizeof(struct Test2) = %lu ", sizeof(struct Test2)); return 0; } /* 输出结果 * sizeof(struct Test1) = 12 * sizeof(struct Test2) = 8 */
②为什么需要内存对齐?
-
- CPU对内存的读取不是连续的,而是分块读取的,块的大小只能是1、2、4、8、16字节
- 当读取操作的数据未对齐,则需要两次总线周期来访问内存,因此性能会大打折扣
- 某些硬件平台只能从规定的地址处取某些特定类型的数据,否则则抛出硬件异常
#pragma pack能够改变编译器的默认对齐方式
#include <stdio.h> #pragma pack(2) struct Test1 { char c1; short s; char c2; int i; }; #pragma pack() #pragma pack(4) struct Test2 { char c1; char c2; short s; int i; }; #pragma pack() int main(int argc, char** argv) { printf("sizeof(struct Test1) = %lu ", sizeof(struct Test1)); printf("sizeof(struct Test2) = %lu ", sizeof(struct Test2)); return 0; } /* * 输出结果: * sizeof(struct Test1) = 10 * sizeof(struct Test2) = 8 */
- struct 占用的内存大小
- 第一个成员起始于0偏移处
- 每个成员按其类型大小和指定对齐参数n中较小的一个进行对齐
- 偏移地址和成员占用大小均需对齐
- 结构体成员的对齐参数为其所有成员使用的对其参数的最大值
- 结构体的总长度必须为所有对其参数的整数倍
#include <stdio.h> #pragma pack(8) struct S1 { short a; long b; }; struct S2 { char c; struct S1 d; double e; }; #pragma pack() int main() { struct S2 s2; printf("sizeof(struct S1) = %lu ", sizeof(struct S1)); printf("sizeof(struct S2) = %lu ", sizeof(struct S2)); printf("%d ", sizeof(long)); printf("%d ", (int)&(s2.d) - (int)&(s2.c)); return 0; } /* * 输出结果: * sizeof(struct S1) = 16 * sizeof(struct S2) = 32 * 8 * 8 */
aligned属性被设置的对象占用更多的空间,相反的,使用packed可以减小对象占用的空间。
2. packed:
使用该属性对struct或union类型进行定义,设定其类型的每一个变量的内存约束。当用在enum类型定义时,暗示了应该使用最小完整的类型(it indicates that the smallest integral type should be used)。
#include <stdio.h> struct unpacked_struct { char c; int i; }; struct packed_struct_1 { char c; int i; } __attribute__((__packed__)); struct packed_struct_2 { char c; int i; struct unpacked_struct us; } __attribute__((__packed__)); int main(int argc, char** argv) { printf("sizeof(struct unpacked_struct) = %lu ", sizeof(struct unpacked_struct)); printf("sizeof(struct packed_struct_1) = %lu ", sizeof(struct packed_struct_1)); printf("sizeof(struct packed_struct_2) = %lu ", sizeof(struct packed_struct_2)); return 0; }
/*
* 输出:
* sizeof(struct unpacked_struct) = 8
* sizeof(struct packed_struct_1) = 5
* sizeof(struct packed_struct_2) = 13
*/
参考文章:
http://www.cnblogs.com/astwish/p/3460618.html