Using GNU C __attribute__ (ZZ)

本文深入探讨了GCC中的__attribute__机制,包括__attribute__((format(printf,,)))、__attribute__((noreturn))、__attribute__((const))等特性的应用,以及如何与非GNU编译器兼容。

Using GNU C __attribute__

http: www.unixwiz.net techtips gnu-c-attributes.html#refs

One of the best (but little known) features of GNU C is the __attribute__ mechanism, which allows a developer to attach characteristics to function declarations to allow the compiler to perform more error checking. It was designed in a way to be compatible with non-GNU implementations, and we've been using this for  in highly portable code with very good results.

Note that __attribute__ spelled with two underscores before and two after, and there are always sets of parentheses surrounding the contents. There is a good reason for this - see below. Gnu CC needs to use the -Wall compiler directive to enable this (yes, there is a finer degree of warnings control available, but we are very big fans of max warnings anyway).


__attribute__ format

This __attribute__ allows assigning printf-like or scanf-like characteristics to the declared function, and this enables the compiler to check the format string against the parameters provided throughout the code. This is  helpful in tracking down hard-to-find bugs.

There are two flavors:

  • __attribute__((format(printf,,)))
  • __attribute__((format(scanf,,)))

but in practice we use the first one much more often.

The () is the number of the "format string" parameter, and () is the number of the first variadic parameter. To see some examples:

/* like printf() but to standard error only */
extern void eprintf(const char *format, ...)
	__attribute__((format(printf, 1, 2)));  /*  */

/* printf only if debugging is at the desired level */
extern void dprintf(int dlevel, const char *format, ...)
	__attribute__((format(printf, 2, 3)));  /*  */

With the functions so declared, the compiler will examine the argument lists

$ cat test.c
  extern void eprintf(const char *format, ...)
               __attribute__((format(printf, 1, 2)));

  void foo()
  {
      eprintf("s=%s\n", 5);             /*  */

      eprintf("n=%d,%d,%d\n", 1, 2);    /*  */
  }

$ cc -Wall -c test.c
test.c: In function `foo':
test.c:6: warning: format argument is not a pointer (arg 2)
test.c:8: warning: too few arguments for format

Note that the "standard" library functions - printf and the like - are already understood by the compiler by default.

__attribute__ noreturn

This attribute tells the compiler that the function won't ever return, and this can be used to suppress errors about code paths not being reached. The C library functions abort() and exit() are both declared with this attribute:

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

Once tagged this way, the compiler can keep track of paths through the code and suppress errors that won't ever happen due to the flow of control never returning after the function call.

In this example, two nearly-identical C source files refer to an "exitnow()" function that never returns, but without the __attribute__ tag, the compiler issues a warning. The compiler is correct here, because it has no way of knowing that control doesn't return.

$ cat test1.c
extern void exitnow();

int foo(int n)
{
        if ( n > 0 )
	{
                exitnow();
		/*  */
	}
        else
                return 0;
}

$ cc -c -Wall test1.c
test1.c: In function `foo':
test1.c:9: warning: this function may return with or without a value

But when we add __attribute__, the compiler suppresses the spurious warning:

$ cat test2.c
extern void exitnow() __attribute__((noreturn));

int foo(int n)
{
        if ( n > 0 )
                exitnow();
        else
                return 0;
}

$ cc -c -Wall test2.c

__attribute__ const

This attribute marks the function as considering  its numeric parameters. This is mainly intended for the compiler to optimize away repeated calls to a function that the compiler knows will return the same value repeatedly. It applies mostly to math functions that have no static state or side effects, and whose return is solely determined by the inputs.

In this highly-contrived example, the compiler normally  call the square() function in every loop even though we know that it's going to return the same value each time:

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

...
	for (i = 0; i < 100; i++ )
	{
		total += square(5) + i;
	}

By adding __attribute__((const)), the compiler can choose to call the function just once and cache the return value.

In virtually every case, const can't be used on functions that take pointers, because the function is not considering just the function parameters but , and it will almost certainly break the code very badly in ways that will be nearly impossible to track down.

Furthermore, the functions so tagged cannot have any side effects or static state, so things like getchar()or time() would behave very poorly under these circumstances.

Putting them together

Multiple __attributes__ can be strung together on a single declaration, and this is not uncommon in practice. You can either use two separate __attribute__s, or use one with a comma-separated list:

/*  */
extern void die(const char *format, ...)
	__attribute__((noreturn))
	__attribute__((format(printf, 1, 2)));

/*or*/

extern void die(const char *format, ...)
	__attribute__((noreturn, format(printf, 1, 2)));

If this is tucked away safely in a library header file,  programs that call this function receive this checking.

Compatibility with non-GNU compilers

Fortunately, the __attribute__ mechanism was cleverly designed in a way to make it easy to quietly eliminate them if used on platforms other than GNU C. Superficially, __attribute__ appears to have multiple parameters (which would typically rule out using a macro), but the  sets of parentheses effectively make it a single parameter, and in practice this works very nicely.

/* If we're not using GNU C, elide __attribute__ */
#ifndef __GNUC__
#  define  __attribute__(x)  /*NOTHING*/
#endif

Note that __attribute__ applies to function , not , and we're not sure why this is. So when defining a function that merits this treatment, an extra declaration must be used (in the same file):

/*  */
void die(const char *format, ...) __attribute__((noreturn))
                                  __attribute__((format(printf,1,2)));

void die(const char *format, ...)
{
	/*  */
}

Other References

We'll note that there are many more attributes available, including those for variables and types, and they are not covered here: we have chosen to just touch on the high points. Those wishing more information can find it in the GNU online documentation at http://gcc.gnu.org:

GCC 4.0
GCC 4.0 Function Attributes 
GCC 4.0 Variable Attributes 
GCC 4.0 Type Attributes 
GCC 3.2
GCC 3.2 Function Attributes 
GCC 3.2 Variable Attributes 
GCC 3.2 Type Attributes
GCC 3.1
GCC 3.1 Function Attributes 
GCC 3.1 Variable Attributes 
GCC 3.1 Type Attributes
GCC 3.0.4
Function Attributes 
Variable Attributes 
Type Attributes
GCC 2.95.3
Function Attributes 
Variable Attributes 
Type Attributes

------------------------------------------------------------------

Additionally, the usage of "weak / alias" will supplied:

Declaring Attributes of Functions

In GNU C, you declare certain things about functions called in your program which help the compiler optimize function calls and check your code more carefully.

The keyword __attribute__ allows you to specify special attributes when making a declaration. This keyword is followed by an attribute specification inside double parentheses. The following attributes are currently defined for functions on all targets: noreturnnoinlinealways_inlinepureconst,nothrowsentinelformatformat_argno_instrument_functionsectionconstructor,destructorusedunuseddeprecatedweakmallocaliaswarn_unused_result and nonnull.Several other attributes are defined for functions on particular target systems. Other attributes, includingsection are supported for variables declarations (see Variable Attributes) and for types (see Type Attributes).

 

alias ("target")
The  alias attribute causes the declaration to be emitted as an alias for another symbol, which must be specified. For instance,
          void __f () { /* Do something. */; }
          void f () __attribute__ ((weak, alias ("__f")));
     

declares `f' to be a weak alias for `__f'. In C++, the mangled name for the target must be used. It is an error if `__f' is not defined in the same translation unit.

Not all target machines support this attribute. 

weak
The  weak attribute causes the declaration to be emitted as a weak symbol rather than a global. This is primarily useful in defining library functions which can be overridden in user code, though it can also be used with non-function declarations. Weak symbols are supported for ELF targets, and also for a.out targets when using the GNU assembler and linker.

FOR MORE:

http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Function-Attributes.html

http://afreez.blog.51cto.com/59057/7351



_attrubte__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。

#define __u8    unsigned char
#define __u16   unsigned short

/* __attribute__ ((packed)) 的位置约束是放于声明的尾部“;”之前 */
struct str_struct{
        __u8    a;
        __u8    b;
        __u8    c;
        __u16   d;
} __attribute__ ((packed));

/*  当用到typedef时,要特别注意__attribute__ ((packed))放置的位置,相当于:
  *  typedef struct str_stuct str;
  *  而struct str_struct 就是上面的那个结构。
  */
typedef struct {
        __u8    a;
        __u8    b;
        __u8    c;
        __u16   d;
} __attribute__ ((packed)) str;

/* 在下面这个typedef结构中,__attribute__ ((packed))放在结构名str_temp之后,其作用是被忽略的,注意与结构str的区别。*/
typedef struct {
        __u8    a;
        __u8    b;
        __u8    c;
        __u16   d;
}str_temp __attribute__ ((packed));

typedef struct {
        __u8    a;
        __u8    b;
        __u8    c;
        __u16   d;
}str_nopacked;

int main(void)
{
        printf("sizeof str_struct   = %d\n", sizeof(struct str_struct));
        printf("sizeof str          = %d\n", sizeof(str));
        printf("sizeof str_temp      = %d\n", sizeof(str_temp));
        printf("sizeof str_nopacked = %d\n", sizeof(str_nopacked));
        return 0;
}

编译运行:
[root@localhost root]# ./packedtest   
sizeof str_struct   = 5 
sizeof str          = 5
sizeof str_temp      = 6
sizeof str_nopacked = 6

--------------------------------------------------------------------
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)是位对齐。


`#ifdef __GNUC__` 是一个预处理指令,用于检查 `__GNUC__` 这个预定义宏是否存在。`__GNUC__` 是 GCC(GNU Compiler Collection)编译器预定义的宏,代表 GCC 的主版本号,`__GNUC_MINOR__` 和 `__GNUC_PATCHLEVEL__` 分别代表 GCC 的次版本号和修正版本号,`__GNUC_PATCHLEVEL__` 从 GCC 3.0 以后才有预定义 [^3][^4]。 该指令在代码中有以下作用: - **实现代码的跨平台兼容性**:在不同的编译器环境下,代码可能需要不同的实现方式。使用 `#ifdef __GNUC__` 可以针对 GCC 编译器编写特定的代码,当代码在 GCC 编译器下编译时,这些特定代码会被包含进来;而在其他编译器下,这些代码则会被忽略,从而提高代码的可移植性。 ```c #ifdef __GNUC__ // GCC 编译器下的特定代码 // 例如使用 GCC 特有的扩展功能 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" int unused_variable = 0; #pragma GCC diagnostic pop #else // 其他编译器下的代码 int used_variable = 0; #endif ``` - **针对特定版本的 GCC 进行代码编写**:结合 `__GNUC__`、`__GNUC_MINOR__` 和 `__GNUC_PATCHLEVEL__` 可以针对特定版本的 GCC 编写代码。例如,某些新的特性可能只在较新的 GCC 版本中支持,通过检查版本号,可以在不同版本的 GCC 下使用不同的代码逻辑。 ```c #ifdef __GNUC__ #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9) // GCC 4.9 及以上版本的代码 // 例如使用 C11 标准的特性 _Static_assert(1 == 1, "This is a static assertion"); #else // GCC 4.9 以下版本的代码 // 使用其他兼容的方式实现相同功能 #endif #endif ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值