__attribute__使用的一些总结

以前用VC时没感觉VC有什么问题,现在终于用到了VC不支持的功能 :-)
现在做个小小的总结
更多见
1.__attribute__ format
2.__attribute__ noreturn
3.__attribute__ const
4.__attribute__ weak
5.__attribute__ constructor
6.__attribute__ destructor

__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 exceptionally helpful in tracking down hard-to-find bugs.

There are two flavors:

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

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

The (m) is the number of the "format string" parameter, and (n) 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)));  /* 1=format 2=params */

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

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

$ cat test.c
1  extern void eprintf(const char *format, ...)
2               __attribute__((format(printf, 1, 2)));
3
4  void foo()
5  {
6      eprintf("s=%s\n", 5);             /* error on this line */
7
8      eprintf("n=%d,%d,%d\n", 1, 2);    /* error on this line */
9  }

$ 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();
		/* control never reaches this point */
	}
        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
no warnings!

__attribute__ const

This attribute marks the function as considering only 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 must 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 also the data the parameters point to, 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:

/* send printf-like message to stderr and exit */
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, all 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 two 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 declarations, not definitions, 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):

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

void die(const char *format, ...)
{
	/* function definition */
}
以上原文http://www.unixwiz.net/techtips/gnu-c-attributes.html
__attribute__ constructor
__attribute__ destructor
#include <stdio.h>
#include <stdlib.h>
static void start(void) __attribute__ ((constructor));
static void stop(void) __attribute__ ((destructor));
int
main(int argc, char *argv[])
{
        printf("start == %p\n", start);
        printf("stop == %p\n", stop);
        exit(EXIT_SUCCESS);
}
void
start(void)
{
        printf("hello world!\n");
}
void
stop(void)
{
        printf("goodbye world!\n");
}
__attribute__ ((weak))
While playing with glibc I noticed one interesting technique. It is
based on gcc's extension that allows you to declare weak symbols. In
essence it works like this:
extern void weak_f (void) __attribute__ ((weak));
 
int main ()
{
  if (weak_f)
  {
    weak_f ();
  }
}
 
 

When you link such code ld won't complain if weak_f is unresolved.
Plus you can check at run-time if symbol has been resolved as shown
above.
 
 
Interesting effects can be achieved using this technique. For example
you can make some code thread-safe on-demand. You compile this code
once and depending on kind of application it is used in it automatically
becomes multi-threaded or single-threaded:
 
 
#include <pthread.h>
 
extern "C" int
pthread_create (pthread_t*,
                const pthread_attr_t*,
                void* (*)(void*),
                void*) __attribute__ ((weak));
 
 
extern "C" int
pthread_mutex_init (pthread_mutex_t*,
                    const pthread_mutexattr_t*) __attribute__ ((weak));
 
 
extern "C" int
pthread_mutex_lock (pthread_mutex_t*) __attribute__ ((weak));
 
 
extern "C" int
pthread_mutex_unlock (pthread_mutex_t*) __attribute__ ((weak));
 
 
extern "C" int
pthread_mutex_destroy (pthread_mutex_t*) __attribute__ ((weak));

class foo
{
public:
 
 
  foo ()
  {
    if (pthread_create) pthread_mutex_init (&m_, 0);
  }
 
  ~foo ()
  {
    if (pthread_create) pthread_mutex_destroy (&m_);
  }
 
 

  bar ()
  {
    if (pthread_create) pthread_mutex_lock (&m_);
 
 
    // ...
 
    if (pthread_create) pthread_mutex_unlock (&m_);
  }
 
 
private:
  pthread_mutex_t m_;
 
 
  // some other data that mutex protects
};
 
 

Now if you link your code with libpthread, foo is automatically
multi-thread-safe. If you don't then all the calls to mutex API
are skipped.
 
 
This could be especially nice if class foo is in the library that
is used by both kinds of applications.
 

高级功能
  * unused
 
  属性 unused 用于函数和变量,表示该函数或变量可能不使用,这个属性可以避免编译器产生警告信息。
 
  * section ("section-name")
 
  属性 section 用于函数和变量,通常编译器将函数放在 .text 节,变量放在.data 或 .bss 节,使用 section 属性,可以让编译器将函数或变量放在指定的节中。例如:
 
++++ include/linux/init.h
78: #define __init          __attribute__ ((__section__ (".text.init")))
79: #define __exit          __attribute__ ((unused, __section__(".text.exit")))
80: #define __initdata      __attribute__ ((__section__ (".data.init")))
81: #define __exitdata      __attribute__ ((unused, __section__ (".data.exit")))
82: #define __initsetup     __attribute__ ((unused,__section__ (".setup.init")))
83: #define __init_call     __attribute__ ((unused,__section__ (".initcall.init")))
84: #define __exit_call     __attribute__ ((unused,__section__ (".exitcall.exit")))
 
  连接器可以把相同节的代码或数据安排在一起,Linux 内核很喜欢使用这种技术,例如系统的初始化代码被安排在单独的一个节,在初始化结束后就可以释放这部分内存。
 
  * aligned (ALIGNMENT)
 
  属性 aligned 用于变量、结构或联合类型,指定变量、结构域、结构或联合的对齐量,以字节为单位,例如:
 
++++ include/asm-i386/processor.h
294: struct i387_fxsave_struct {
295:         unsigned short  cwd;
296:         unsigned short  swd;
297:         unsigned short  twd;
298:         unsigned short  fop;
299:         long    fip;
300:         long    fcs;
301:         long    foo;
......
308: } __attribute__ ((aligned (16)));
 
  表示该结构类型的变量以 16 字节对齐。通常编译器会选择合适的对齐量,显示指定对齐通常是由于体系限制、优化等原因。
 
  * packed
 
  属性 packed 用于变量和类型,用于变量或结构域时表示使用最小可能的对齐,用于枚举、结构或联合类型时表示该类型使用最小的内存。例如:
 
++++ include/asm-i386/desc.h
51: struct Xgt_desc_struct {
52:         unsigned short size;
53:         unsigned long address __attribute__((packed));
54: };
 
  域 address 将紧接着 size 分配。属性 packed 的用途大多是定义硬件相关的结构,使元素之间没有因对齐而造成的空洞。
 

  当前函数名
 
  GNU CC 预定义了两个标志符保存当前函数的名字,__FUNCTION__ 保存函数在源码中的名字__PRETTY_FUNCTION__ 保存带语言特色的名字。在 C 函数中,这两个名字是相同的,在 C++ 函数中,__PRETTY_FUNCTION__ 包括函数返回类型等额外信息,Linux 内核只使用了 __FUNCTION__。
 
++++ fs/ext2/super.c
98: void ext2_update_dynamic_rev(struct super_block *sb)
99: {
100:         struct ext2_super_block *es = EXT2_SB(sb)->s_es;
101:
102:         if (le32_to_cpu(es->s_rev_level) > EXT2_GOOD_OLD_REV)
103:                 return;
104:
105:         ext2_warning(sb, __FUNCTION__,
106:                      "updating to rev %d because of new feature flag, "
107:                      "running e2fsck is recommended",
108:                      EXT2_DYNAMIC_REV);
 
  这里 __FUNCTION__ 将被替换为字符串 "ext2_update_dynamic_rev"。虽然__FUNCTION__ 看起来类似于标准 C 中的 __FILE__,但实际上 __FUNCTION__是被编译器替换的,不象 __FILE__ 被预处理器替换。
 

  内建函数
 
  GNU C 提供了大量的内建函数,其中很多是标准 C 库函数的内建版本,例如memcpy,它们与对应的 C 库函数功能相同,本文不讨论这类函数,其他内建函数的名字通常以 __builtin 开始。
 
  * __builtin_return_address (LEVEL)
 
  内建函数 __builtin_return_address 返回当前函数或其调用者的返回地址,参数LEVEL 指定在栈上搜索框架的个数,0 表示当前函数的返回地址,1 表示当前函数的调用者的返回地址,依此类推。例如:
 
++++ kernel/sched.c
437:                 printk(KERN_ERR "schedule_timeout: wrong timeout "
438:                        "value %lx from %p\n", timeout,
439:                        __builtin_return_address(0));
 
  * __builtin_constant_p(EXP)
 
  内建函数 __builtin_constant_p 用于判断一个值是否为编译时常数,如果参数EXP 的值是常数,函数返回 1,否则返回 0。例如:
 
++++ include/asm-i386/bitops.h
249: #define test_bit(nr,addr) \
250: (__builtin_constant_p(nr) ? \
251:  constant_test_bit((nr),(addr)) : \
252:  variable_test_bit((nr),(addr)))
 
  很多计算或操作在参数为常数时有更优化的实现,在 GNU C 中用上面的方法可以根据参数是否为常数,只编译常数版本或非常数版本,这样既不失通用性,又能在参数是常数时编译出最优化的代码。
 
  * __builtin_expect(EXP, C)
 
  内建函数 __builtin_expect 用于为编译器提供分支预测信息,其返回值是整数表达式 EXP 的值,C 的值必须是编译时常数。例如:
 
++++ include/linux/compiler.h
13: #define likely(x)       __builtin_expect((x),1)
14: #define unlikely(x)     __builtin_expect((x),0)
++++ kernel/sched.c
564:         if (unlikely(in_interrupt())) {
565:                 printk("Scheduling in interrupt\n");
566:                 BUG();
567:         }
 
  这个内建函数的语义是 EXP 的预期值是 C,编译器可以根据这个信息适当地重排语句块的顺序,使程序在预期的情况下有更高的执行效率。上面的例子表示处于中断上下文是很少发生的,第 565-566 行的目标码可能会放在较远的位置,以保证经常执行的目标码更紧凑。
<script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/buttonLite.js#style=-1&uuid=&pophcol=3&lang=zh"></script> <script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/bshareC0.js"></script>
阅读(2160) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值