Using GNU C __attribute__ 阅读笔记
Using GNU C __attribute__ reading notes
1. 本文讲述GNU编译器的一个特性-__attribute__,这个宏是一个编译器指令,我们在代码中通过定义这个东西,可以inform编译器我们代码 的一些逻辑,从而在编译器避免一些错误,在运行期提高性能。__attribute__在很多代码中都有应用,非常实用。包括我们在看一些老外写的比较成 熟的代码的时候,都能看到这个东西。
2. __attribute__ format ,这个东西能告知编译器,我们的代码在处理printf,scanf这样变参数的函数的时候,哪个参数是format string,哪个参数是参数列表,这样可以避免代码中的一些问题,比如:
从上面可以看出,我们定义了eprintf函数,第一个参数是Format String,第二个参数是对应Format String的参数列表,下面的dprintf也是一样,这样一来,编译器就能检测出下面这样的代码错误:
3. __attribute__ noreturn,这是是告诉编译器一个函数是没有return的,我觉得用处不大,自己看附件英文原文吧。
4. __attribute__ const, 这个东西能告诉编译器,在给定参数的情况下,这个function始终返回同样的值。这样可以帮助程序提高性能,比如:
如 果我们在square函数中没有定义__attribute__ const的话,在下面的那个循环中,程序每次都要产生一个调用square函数的代码。但是这里指定了const之后,程序就知道,对于同一个输入参数 5,返回值都是一样的。这样程序就会执行一次square,然后cache这个函数的return value,这样下次循环开始,对square函数的调用就没有函数调用的逻辑了,直接返回上次的结果。
5. 混用上面的东西,下面两种写法都可以:
6. 使用__attribute__带来的程序可移植性的问题,由于__attribute__是GNU编译器专用的,所以如果我们这样的代码要移植到非GNU的环境下,怎么办呢?很简单,使用条件编译:
/* If we're not using GNU C, elide __attribute__ */
#ifndef __GNUC__
# define __attribute__(x) /*NOTHING*/
#endif
这 里define __attribute__(x),看到了吧?__attribute__只接受一个参数,但我们看到在使用format,const这些东西的时候,明 明有好几个参数啊?再仔细看,我们在使用__attribute__的时候,在format,const这些关键字的外面还有一层括号,也就是说,我们在 每个__attribute__的里面,都用了两层括号,包括了format,const这些东西的那层括号就指明了括号中的内容是一个参数,这样就符合 这里的定义了。注:这样的做法在windows平台下也很常见,应该说这是C语言编程,特别是宏编程中一个非常常用的技巧,通过这样的技巧,就可以把一个 不定参数的东西定义成只有一个参数的宏原型。
7. 一点注意:
在 使用__attribute__的时候,要注意的是,我们必须在函数的申明处使用__attribute__,在函数的实现处,是不能定义的--也就是 说,对于每个想使用__attribute__特性的函数,必须手动定义一个函数申明,然后是函数实现,光写一个函数实现是不可以的,因为 __attribute__不能被定义在函数实现上(不知道为什么,GNU的C编译器就是这样的),比如:
1. 本文讲述GNU编译器的一个特性-__attribute__,这个宏是一个编译器指令,我们在代码中通过定义这个东西,可以inform编译器我们代码 的一些逻辑,从而在编译器避免一些错误,在运行期提高性能。__attribute__在很多代码中都有应用,非常实用。包括我们在看一些老外写的比较成 熟的代码的时候,都能看到这个东西。
2. __attribute__ format ,这个东西能告知编译器,我们的代码在处理printf,scanf这样变参数的函数的时候,哪个参数是format string,哪个参数是参数列表,这样可以避免代码中的一些问题,比如:
- Code: Select all
/* 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 */
从上面可以看出,我们定义了eprintf函数,第一个参数是Format String,第二个参数是对应Format String的参数列表,下面的dprintf也是一样,这样一来,编译器就能检测出下面这样的代码错误:
- Code: Select all
$ 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
3. __attribute__ noreturn,这是是告诉编译器一个函数是没有return的,我觉得用处不大,自己看附件英文原文吧。
4. __attribute__ const, 这个东西能告诉编译器,在给定参数的情况下,这个function始终返回同样的值。这样可以帮助程序提高性能,比如:
- Code: Select all
extern int square(int n) __attribute__((const));
...
for (i = 0; i < 100; i++ )
{
total += square(5) + i;
}
如 果我们在square函数中没有定义__attribute__ const的话,在下面的那个循环中,程序每次都要产生一个调用square函数的代码。但是这里指定了const之后,程序就知道,对于同一个输入参数 5,返回值都是一样的。这样程序就会执行一次square,然后cache这个函数的return value,这样下次循环开始,对square函数的调用就没有函数调用的逻辑了,直接返回上次的结果。
5. 混用上面的东西,下面两种写法都可以:
- Code: Select all
/* 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)));
6. 使用__attribute__带来的程序可移植性的问题,由于__attribute__是GNU编译器专用的,所以如果我们这样的代码要移植到非GNU的环境下,怎么办呢?很简单,使用条件编译:
/* If we're not using GNU C, elide __attribute__ */
#ifndef __GNUC__
# define __attribute__(x) /*NOTHING*/
#endif
这 里define __attribute__(x),看到了吧?__attribute__只接受一个参数,但我们看到在使用format,const这些东西的时候,明 明有好几个参数啊?再仔细看,我们在使用__attribute__的时候,在format,const这些关键字的外面还有一层括号,也就是说,我们在 每个__attribute__的里面,都用了两层括号,包括了format,const这些东西的那层括号就指明了括号中的内容是一个参数,这样就符合 这里的定义了。注:这样的做法在windows平台下也很常见,应该说这是C语言编程,特别是宏编程中一个非常常用的技巧,通过这样的技巧,就可以把一个 不定参数的东西定义成只有一个参数的宏原型。
7. 一点注意:
在 使用__attribute__的时候,要注意的是,我们必须在函数的申明处使用__attribute__,在函数的实现处,是不能定义的--也就是 说,对于每个想使用__attribute__特性的函数,必须手动定义一个函数申明,然后是函数实现,光写一个函数实现是不可以的,因为 __attribute__不能被定义在函数实现上(不知道为什么,GNU的C编译器就是这样的),比如:
- Code: Select all
/* function declaration */
void die(const char *format, ...) __attribute__((noreturn))
__attribute__((format(printf,1,2)));
void die(const char *format, ...)
{
/* function definition */
}