分类: LINUX
2014-10-11 10:21:37
可以在同一个函数声明里使用多个__attribute__,并且实际应用中这种情况是十分常见的。使用方式上,你可以选择两个单独的__attribute__,或者把它们写在一起,可以参考下面的例子:
/* 把类似printf的消息传递给stderr 并退出 */
extern void die(const char *format, ...)
__attribute__((noreturn))
__attribute__((format(printf, 1, 2)));
或者写成
extern void die(const char *format, ...)
__attribute__((noreturn, format(printf, 1, 2)));
如果带有该属性的自定义函数追加到库的头文件里,那么所以调用该函数的程序都要做相应的检查。
庆幸的是,__attribute__设计的非常巧妙,很容易作到和其它编译器保持兼容,也就是说,如果工作在其它的非GNU编译器上,可以很容易的忽略该属性。即使__attribute__使用了多个参数,也可以很容易的使用一对圆括弧进行处理,例如:
/* 如果使用的是非GNU C, 那么就忽略__attribute__ */
#ifndef __GNUC__
# define __attribute__(x) /*NOTHING*/
#endif
需要说明的是,__attribute__适用于函数的声明而不是函数的定义。所以,当需要使用该属性的函数时,必须在同一个文件里进行声明,例如:
/* 函数声明 */
void die(const char *format, ...) __attribute__((noreturn))
__attribute__((format(printf,1,2)));
void die(const char *format, ...)
{
/* 函数定义 */
}
更多的属性含义参考:
关键字__attribute__也可以对变量(variable)或结构体成员(structure field)进行属性设置。这里给出几个常用的参数的解释,更多的参数可参考本文给出的连接。
在使用__attribute__参数时,你也可以在参数的前后都加上“__”(两个下划线),例如,使用__aligned__而不是aligned,这样,你就可以在相应的头文件里使用它而不用关心头文件里是否有重名的宏定义。
该属性规定变量或结构体成员的最小的对齐格式,以字节为单位。例如:
int x __attribute__ ((aligned (16))) = 0;
编译器将以16字节(注意是字节byte不是位bit)对齐的方式分配一个变量。也可以对结构体成员变量设置该属性,例如,创建一个双字对齐的int对,可以这么写:
struct foo { int x[2] __attribute__ ((aligned (8))); };
如上所述,你可以手动指定对齐的格式,同样,你也可以使用默认的对齐方式。如果aligned后面不紧跟一个指定的数字值,那么编译器将依据你的目标机器情况使用最大最有益的对齐方式。例如:
short array[3] __attribute__ ((aligned));
选择针对目标机器最大的对齐方式,可以提高拷贝操作的效率。
aligned属性使被设置的对象占用更多的空间,相反的,使用packed可以减小对象占用的空间。
需要注意的是,attribute属性的效力与你的连接器也有关,如果你的连接器最大只支持16字节对齐,那么你此时定义32字节对齐也是无济于事的。
使用该属性可以使得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位对齐。
下面的例子中,x成员变量使用了该属性,则其值将紧放置在a的后面:
struct test
{
char a;
int x[2] __attribute__ ((packed));
};
其它可选的属性值还可以 是:cleanup,common,nocommon,deprecated,mode,section,shared,tls_model,transparent_union,unused,vector_size,weak,dllimport,dlexport 等,
详细信息可参考:
关键字__attribute__也可以对结构体(struct)或共用体(union)进行属性设置。大致有六个参数值可以被设定, 即:aligned, packed, transparent_union, unused, deprecated 和 may_alias。
在使用__attribute__参数时,你也可以在参数的前后都加上“__”(两个下划线),例如,使用__aligned__而不是aligned,这样,你就可以在相应的头文件里使用它而不用关心头文件里是否有重名的宏定义。
该属性设定一个指定大小的对齐格式(以字节为单位),例如:
struct S { short f[3]; } __attribute__ ((aligned (8)));
typedef int more_aligned_int __attribute__ ((aligned (8)));
该声明将强制编译器确保(尽它所能)变量类型为struct S或者more-aligned-int的变量在分配空间时采用8字节对齐方式。
如上所述,你可以手动指定对齐的格式,同样,你也可以使用默认的对齐方式。如果aligned后面不紧跟一个指定的数字值,那么编译器将依据你的目标机器情况使用最大最有益的对齐方式。例如:
struct S { short f[3]; } __attribute__ ((aligned));
这里,如果sizeof(short)的大小为2(byte),那么,S的大小就为6。取一个2的次方值,使得该值大于等于6,则该值为8,所以编译器将设置S类型的对齐方式为8字节。
aligned属性使被设置的对象占用更多的空间,相反的,使用packed可以减小对象占用的空间。
需要注意的是,attribute属性的效力与你的连接器也有关,如果你的连接器最大只支持16字节对齐,那么你此时定义32字节对齐也是无济于事的。
使用该属性对struct或者union类型进行定义,设定其类型的每一个变量的内存约束。当用在enum类型定义时,暗示了应该使用最小完整的类 型(it indicates that the smallest integral type should be used)。
下面的例子中,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__属性定义了一些结构体及其变量,并给出了输出结果和对结果的分析。
程序代码为:
struct p
{
int a;
char b;
char c;
}__attribute__((aligned(4))) pp;
struct q
{
int a;
char b;
struct n qn;
char c;
}__attribute__((aligned(8))) qq;
int main()
{
printf("sizeof(int)=%d,sizeof(short)=%d.sizeof(char)=%d\n",sizeof(int),sizeof(short),sizeof(char));
printf("pp=%d,qq=%d \n", sizeof(pp),sizeof(qq));
return 0;
}
输出结果:
sizeof(int)=4,sizeof(short)=2.sizeof(char)=1
pp=8,qq=24
分析:
sizeof(pp):
sizeof(a)+ sizeof(b)+ sizeof(c)=4+1+1=6<23=8= sizeof(pp)
sizeof(qq):
sizeof(a)+ sizeof(b)=4+1=5
sizeof(qn)=8;即qn是采用8字节对齐的,所以要在a,b后面添3个空余字节,然后才能存储qn,
4+1+(3)+8+1=17
因为qq采用的对齐是8字节对齐,所以qq的大小必定是8的整数倍,即qq的大小是一个比17大又是8的倍数的一个最小值,由此得到
17<24+8=24= sizeof(qq)