GNU C中的__attribute__机制简介
当前位置:以往代写 > C/C++ 教程 >GNU C中的__attribute__机制简介
2019-06-13

GNU C中的__attribute__机制简介

GNU C中的__attribute__机制简介

副标题#e#

1. __attribute__

GNU C的一大特色(却不被初学者所知)就是 __attribute__机制。

__attribute__可以配置函数属性(Function Attribute)、变量属性(Variable Attribute)和范例属性(Type Attribute)

__attribute__前后都有两个下划线,而且后头会紧跟一对原括弧,括弧 内里是相应的__attribute__参数

__attribute__语法名目为:

__attribute__ ( ( attribute-list ) )

函数属性(Function Attribute),函数属性可以辅佐开拓者把一些特性添加到函数声明中,从而可以 使编译器在错误查抄方面的成果更强大。

__attribute__机制也很容易同 非GNU应用措施做到兼容。

GNU CC需要利用 –Wall,这是节制告诫信息的 一个很好的方法。下面先容几个常见的属性参数。

2. format

该属 性可以使编译器查抄函数声明和函数实际挪用参数之间的名目化字符串是否匹配 。它可以给被声明的函数加上雷同printf可能scanf的特征,该成果十分有用,尤 其是处理惩罚一些很难发明的bug。

format的语法名目为:

format ( archetype,  string-index,  first-to-check )

format属性 汇报编译器,凭据printf,scanf,strftime或strfmon的参数表名目法则对该函 数的参数举办查抄。archetype:指定是哪种气势气魄;

string-index:指定 传入函数的第几个参数是名目化字符串;

first-to-check:指定从函数的 第几个参数开始按上述法则举办查抄。

详细利用名目如下:

__attribute__( ( format( printf,m,n ) ) )

__attribute__( ( format( scanf,m,n ) ) )

个中参数m与n的寄义为:

m:第几 个参数为名目化字符串(format string);

n:参数荟萃中的第一个,即 参数“…”里的第一个参数在函数参数总数排在第几

留意,有时函数参数 里尚有“隐身”的呢,后头会提到;

在利用上,__attribute__((format (printf,m,n)))是常用的,而另一种却很少见到。

下面举例说明,个中 myprint为本身界说的一个带有可变参数的函数,其成果雷同于printf:

//m=1;n=2

extern void  myprint( const char *format, … ) __attribute__( ( format( printf,1,2 ) ) );

//m=2; n=3

extern void  myprint( int l,const char *format,… ) __attribute__( ( format( printf,2,3 ) ) );

需要出格留意的是,如 果myprint是一个函数的成员函数,那么m和n的值可有点“悬乎”了,譬喻:

//m=3;n=4

extern void  myprint( int l,const char *format,… ) __attribute__( ( format( printf,3,4 ) ) );

其原 因是,类成员函数的第一个参数实际上一个“隐身”的“this”指针。(有点C++ 基本的都知道点this指针,不知道你在这里还知道吗?)
这里给出测试用 例:attribute.c,代码如下:

extern void myprint(const char *format,…) __attribute__((format(printf,1,2)));

void test()

{

        myprint("i=% d/n",6);

        myprint("i=% s/n",6);

        myprint("i=% s/n","abc");

       myprint ("%s,%d,%d/n",1,2);

}


#p#副标题#e#

运行$gcc –Wall –c attribute.c attribute后,输出功效为:

attribute.c: In function `test’:

attribute.c:7: warning: format argument is not a pointer (arg 2)

attribute.c:9: warning: format argument is not a pointer (arg 2)

attribute.c:9: warning: too few arguments for format

假如在attribute.c中的函数声明去掉__attribute__((format (printf,1,2))),再从头编译,

既运行$gcc –Wall –c attribute.c attribute后,则并不会输出任何告诫信息。

留意,默认环境下,编译器 是能识别雷同printf的“尺度”库函数。

3. noreturn

该属性通知 编译器函数从不返回值。

当碰着函数需要返回值却还没运行到返回值处就 已退出来的环境,该属性可以制止呈现错误信息。C库函数中的abort()和exit ()的声明名目就回收了这种名目:

extern void  exit(int)   __attribute__( ( noreturn ) );

extern void  abort (void)  __attribute__( ( noreturn ) );

为了利便领略,各人可 以参考如下的例子:

//name: noreturn.c     ;测试 __attribute__((noreturn))

extern void  myexit();

int  test( int  n )

{

   if ( n > 0 )

   {

           myexit();

           /* 措施 不行能达到这里 */

   }

   else

   {

          return 0;

   }

}

编译$gcc –Wall –c noreturn.c  显示的输出信息为:

noreturn.c: In function `test’:

noreturn.c:12: warning: control reaches end of non- void function

#p#分页标题#e#

告诫信息也很好领略,因为你界说了一个有返回值的函数 test却有大概没有返回值,措施虽然不知道怎么办了!加上__attribute__ ((noreturn))则可以很好的处理惩罚雷同这种问题。把extern void myexit();修改为 :

extern void  myexit() __attribute__((noreturn));

之 后,编译不会再呈现告诫信息。

4. const

该属性只能用于带有数 值范例参数的函数上,当反复挪用带有数值参数的函数时,由于返回值是沟通的 。所以此时编译器可以举办优化处理惩罚,除第一次需要运算外, 其它只需要返回第 一次的功效。

该属性主要合用于没有静态状态(static state)和副浸染 的一些函数,而且返回值仅仅依赖输入的参数。为了说明问题,下面举个很是“ 糟糕”的例子,该例子将反复挪用一个带有沟通参数值的函数,详细如下:

extern int  square( int  n ) __attribute__ ( (const) );

for (i = 0; i < 100; i++ )                  

{      

      total += square (5) + i;            

}

添加__attribute__((const))声明,编 译器只挪用了函数一次,今后只是直接获得了沟通的一个返回值。

事实上 ,const参数不能用在带有指针范例参数的函数中,因为该属性不单影响函数的参 数值,同样也影响到了参数指向的数据,它大概会对代码自己发生严重甚至是不 可规复的严重效果。而且,带有该属性的函数不能有任何副浸染可能是静态的状 态,雷同getchar()或time()的函数是不适合利用该属性。

#p#副标题#e#

5. finstrument-functions

该参数可以使措施在编译时,在函数的进口和出 口处生成instrumentation挪用。刚亏得函数进口之后并刚亏得函数出口之前,将 利用当前函数的地点和挪用地点来挪用下面的profiling函数。(在一些平台上, __builtin_return_address不能在高出当前函数范畴之外正常事情,所以挪用地 址信息大概对profiling函数是无效的)

void  __cyg_profile_func_enter( void  *this_fn,void  *call_site );

void  __cyg_profile_func_exit( void  *this_fn,void  *call_site );

个中,第一个参数this_fn 是当前函数的起始地点,可在标记表中找到;第二个参数call_site是挪用处地点 。

6. instrumentation

也可用于在其它函数中展开的内联函数。 从观念上来说,profiling挪用将指出在那边进入和退出内联函数。这就意味着这 种函数必需具有可寻址形式。假如函数包括内联,而所有利用到该函数的措施都 要把该内联展开,这会特别地增加代码长度。假如要在C 代码中利用extern inline声明,必需提供这种函数的可寻址形式。
可对函数指定 no_instrument_function属性,在这种环境下不会举办 instrumentation操纵。 譬喻,可以在以下环境下利用no_instrument_function属性:上面列出的 profiling函数、高优先级的间断例程以及任何不能担保profiling正常挪用的函 数。

no_instrument_function

假如利用了-finstrument- functions,将在绝大大都用户编译的函数的进口和出口点挪用profiling函数。 利用该属性,将不举办instrument操纵。

7. constructor/destructor

若函数被设定为constructor属性,则该函数会 在main()函数执行之前被自动的执行。雷同的,若函数被设定为destructor属 性,则该函数会在main()函数执行之后可能exit()被挪用后被自动的执行。 拥有此类属性的函数常常隐式的用在措施的初始化数据方面,这两个属性还没有 在面向工具C中实现。

8. 同时利用多个属性

可以在同一个函数声 明里利用多个__attribute__,而且实际应用中这种环境是十分常见的。利用方法 上,你可以选择两个单独的__attribute__,可能把它们写在一起,可以参考下面 的例子:

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)) );

假如带有该属性的自界说函数追加到库的头文件里 ,那么所以挪用该函数的措施都要做相应的查抄。

9. 和非GNU编译器的兼 容性

__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,… )

{   /* 函数界说 */  }

更多属性参考: http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Function-Attributes.html

10. 变量属性(Variable Attributes)

要害字__attribute__也可以 对变量(variable)或布局体成员(structure field)举办属性配置。

#p#分页标题#e#

在利用__attribute__参数时,你也可以在参数的前后都加上“__”(两个下划线 ),譬喻,利用__aligned__而不是aligned,这样,你就可以在相应的头文件里 利用它而不消体贴头文件里是否有重名的宏界说。

11. 范例属性(Type Attribute)

要害字__attribute__也可以对布局体(struct)或共用体( union)举办属性配置。

大抵有六个参数值可以被设定:aligned,packed ,transparent_union,unused,deprecated,may_alias

#p#副标题#e#

12. aligned (alignment)

该属性设定一个指定巨细的对齐名目(以字节为单元),例 如:

struct S { short f[3]; } __attribute__ ( ( aligned (8) ) );

typedef  int  more_aligned_int __attribute__ ( ( aligned (8) ) );

这里,假如sizeof(short)的巨细为2(byte),那么 ,S的巨细就为6。取一个2的次方值,使得该值大于便是6,则该值为8,所以编译 器将配置S范例的对齐方法为8字节。该声明将强制编译器确保(尽它所能)变量 范例为struct S可能more-aligned-int的变量在分派空间时回收8字节对齐方法。

如上所述,你可以手动指定对齐的名目,同样,你也可以利用默认的对齐 方法。譬喻:

struct S { short f[3]; } __attribute__ ( (aligned) );

上面,aligned后头不紧跟一个指定的数字值,编译器将依据你的方针 呆板环境利用最大最有益的对齐方法。

int  x __attribute__ ( (aligned (16) ) )  =  0;

编译器将以16字节(留意是字节 byte不是位bit)对齐的方法分派一个变量。也可以对布局体成员变量配置该属性 ,譬喻,建设一个双字对齐的int对,可以这么写:

Struct  foo {  int  x[2] __attribute__ ( (aligned (8) ) );  };

选择针对方针呆板最大的对齐方法,可以提高拷贝操纵的效率。
aligned属 性使被配置的工具占用更多的空间,相反的,利用packed可以减小工具占用的空 间。
需要留意的是,attribute属性的效力与你的毗连器也有关,假如你的 毗连器最大只支持16字节对齐,那么你此时界说32字节对齐也是无济于事的。

13. packed

利用该属性可以使得变量可能布局体成员利用最小的 对齐方法,即对变量是一字节对齐,对域(field)是位对齐。利用该属性对 struct可能union范例举办界说,设定其范例的每一个变量的内存约束。当用在 enum范例界说时,体现了应该利用最小完整的范例 (it indicates that the smallest integral type should be used)。

下面的例子中,x成员变量 利用了该属性,则其值将紧安排在a的后头:

struct  test

{

     char  a;

     int  x[2] __attribute__ ((packed));

};

下面的 例子中,my-packed-struct范例的变量数组中的值将会牢牢的靠在一起,但内部 的成员变量s不会被“pack”,假如但愿内部的成员变量也被packed,my- unpacked-struct也需要利用packed举办相应的约束。

struct my_packed_struct

{

       char  c;

       int  i;

       struct  my_unpacked_struct  s;

} __attribute__ ( (__packed__) );

其它可选的属性值还可以是:cleanup ,common,nocommon,deprecated,mode,section,shared, tls_model, transparent_union,unused,vector_size,weak,dllimport,dlexport等。

更多具体参考:http://gcc.gnu.org/onlinedocs/gcc- 4.0.0/gcc/Variable-Attributes.html#Variable-Attributes

14. 变量属性与范例属性举例

下面的例子中利用__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;

}

#p#副标题#e#

输出功效:

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<2^3=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<2^4+8=24= sizeof(qq)

更具体的先容见:http://gcc.gnu.org/

下面是一些便捷的毗连:

GCC 4.0 Function Attributes

GCC 4.0 Variable Attributes

GCC 4.0 Type Attributes

15. Ref

简朴__attribute__先容: http://www.unixwiz.net/techtips/gnu-c-attributes.html

具体__attribute__先容:http://gcc.gnu.org/

    关键字:

在线提交作业