《Using the GNU Compiler Collection》
google 中搜索
Using the GNU Compiler Collection 4.8.5 pdf
gcc 4.8.5 (CentOS7 中使用的版本)
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 years in highly portable code with very good results.
Note that__attribute__
spelled with two underscores before and two after, and there are always two 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__
机制,它的作用是允许开发者在函数声明的时候,指定一些特性的编译器指令,它可以让编译器在编译的时候进行更多的错误检查和高级优化工作。主要描述 __attribute__
属性机制在 C 语言中的语法,C++ 和 Objective-C 一些细节上略有不同。C++ 中,当__attribute__
属性机制与重载和模板一起
使用时,可能会出现问题。将来 C++ 对属性的支持可能只局限于声明中的属性,而在嵌套声明器中不支持 。
__attribute__
语法格式
属性说明符(attribute specifier) 的形式为:
__attribute__ ((attribute-list))
attribute-list 可以是用逗号(,)分隔多个属性
attribute
前后各有两条下划线 ,然后后面跟两组括号(两个括号可以让它很容易在宏里面使用,特别是有多个属性的时候)。
__attribute__
指令可以放在函数,变量和类型等声明之后,分号(;
)之前。
Gnu CC
需要使用 -Wall
编译器指令来启用它
属性说明符列表可以作为结构体、联合体或枚举说明符的一部分出现。它可以紧跟在 struct,union
或 enum
关键字之后, 也可以紧跟在右大括之后(after the closing brace
),
分号之前
struct S {
short b[3];
} __attribute__ ((aligned (8)));
typedef int int32_t __attribute__ ((aligned (8)));
__attribute__
指令可以放在函数,变量和类型声明之后
注意,gcc 的不同版本对 __attribute__
的使用可能有微小的差异,可以对应着 gcc 的版本查阅文档
GNU C
和 C++
中,您可以使用函数属性来指定某些函数属性,这些属性可以帮助编译器优化调用或更仔细地检查代码的正确性。例如:
函数属性(Function Attribute) | 类型属性(Type Attributes) | 变量属性(Variable Attribute) | Clang特有的 |
---|---|---|---|
noreturn | aligned | alias | availability |
noinline | packed | at(address) | overloadable |
always_inline | bitband | aligned | |
flatten | deprecated | ||
pure | noinline | ||
const | packed | ||
constructor | weak | ||
destructor | weakref(“target”) | ||
sentinel | section(“name”) | ||
format | unused | ||
format_arg | used | ||
section | visibility(“visibility_type”) | ||
used | zero_init | ||
unused | |||
deprecated | |||
weak | |||
malloc | |||
alias | |||
warn_unused_result | |||
nonnull | |||
nothrow (不抛出C++ 异常) |
__attribute__ unused
功能
使用示例
/* warning: ‘someFunction‘ declared ‘static‘ but never defined */
static int someFunction() __attribute__((unused));
int main(int argc __attribute__((unused)), char **argv)
{
/* warning: unused variable ‘mypid‘ */
int mypid __attribute__((unused)) = getpid();
#ifdef DEBUG
printf("My PID = %d\n", mypid);
#endif
return 0;
}
__attribute__ format
功能
printf
或者 scanf
的特征,它可以使编译器检查函数声明和函数实际调用参数之间的格式化字符串是否匹配。format
属性告诉编译器,按照 printf, scanf
等标准 C 函数参数格式规则对该函数的参数进行检查。这在我们自己封装调试信息的接口时非常的有用。printf
之类的函数——在默认情况下已经被编译器理解了。format 的语法格式为:
format (archetype, string-index, first-to-check)
其中:
“archetype”指定是哪种风格;
“string-index”指定传入函数的第几个参数是格式化字符串;
“first-to-check”指定从函数的第几个参数开始按上述规则进行检查。
有两种风格,第一种风格用得更多:
__attribute__((format(printf,m,n)))
__attribute__((format(scanf,m,n)))
其中参数 m 与 n 的含义为:
m:自定义函数声明的第几个参数为格式化字符串(format string);
n:自定义函数声明的第一个可变参数的位置,即参数“…”里的第一个参数在函数参数总数排在第几。
使用示例
/* 该函数的,参数类似printf(),但只输出到标准错误*/
extern void eprintf(const char *format, ...)
__attribute__((format(printf, 1, 2))); /* 1=format 2=params */
/*该函数只有在调试达到所需级别时才打印*/
extern void dprintf(int dlevel, const char *format, ...)
__attribute__((format(printf, 2, 3))); /* 2=format 3=params */
/*dpinger 源码中的 fatal 输出 */
__attribute__ ((noreturn, format (printf, 1, 2)))
static void fatal(const char * format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(EXIT_FAILURE);
}
__attribute__ noreturn
功能
示例
C
库函数 abort()
和 exit()
都使用这个属性声明:
extern void exit(int) __attribute__((noreturn));
extern void abort(void) __attribute__((noreturn));
在这个例子中,两个几乎相同的 C 源文件引用了一个从不返回的 "exitnow()" 函数,但是如果没有 __attribute__
标记,编译器会发出警告。编译器在这里是正确的,
因为它无法知道控件没有返回。
/*$ 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:12:1: warning: control reaches end of non-void function [-Wreturn-type]}
/*添加 __attribute__((noreturn)) 后,编译输出无警告*/
/*$ 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
功能
注意
示例:在这个高度人为的例子中,编译器通常必须在每个循环中调用 square()
函数,即使我们知道它每次返回的值都是相同的:
extern int square(int n) __attribute__((const));
...
for (i = 0; i < 100; i++ )
{
total += square(5) + i; // square(5) 每次都返回相同的值,通过添加__attribute__((const)),告诉GCC,对于具有相同参数值的 square 函数,后续调用可以
// 被第一次调用的结果所取代,而不管它们之间的语句是什么,编译器可以选择只调用函数一次,并缓存返回值。
}
__attribute__ nonnull (arg-index, ...)
nonnull
属性指定一些函数形参应该是非空指针nonnull
属性可应用于具有至少一个指针类型参数的函数。nonnull (arg-index, ...)
arg-index ,指针参数在你定义的函数的参数列表中的位置extern void *my_memcpy (void *dest, const void *src, size_t len)__attribute__((nonnull (1, 2)));
表明,第一个参数,和第二个参数都应该是非空指针
__attribute__ aligned(alignment)
功能
alignment
的值只能是 2 的 n 次幂,n 为正整数。最小对其单位为 2 字节。aligned
前后加上下划线,这允许您在头文件中使用这些属性,而不必关心可能同名的宏。例如,你可以使用 __aligned__
代替 aligned
__attribute__ ((aligned(8))) 等同于 __attribute__ ((__aligned__(8)))
示例
int x __attribute__ ((aligned (16))) = 0; // 使编译器在 16 字节边界上分配全局变量x, 即定义变量在内存中的地址总能被 16 整除。占用的空间大小不变
typedef int int32_t __attribute__ ((aligned (8))); // 即定义变量在内存中的地址总能被 8 整除,占用的空间大小不变
struct S1 {
short b[3];
int a;
};// 默认按照 sizeof(int) = 4 对齐
struct S2 {
short b[3];
int a;
} __attribute__ ((aligned(8))); //按照 8 字节对齐
printf("struct S1 size: %ld\n",sizeof(struct S1));
printf("struct S2 size: %ld\n",sizeof(struct S2));
输出:
struct S1 size: 12
struct S2 size: 16
关于内存对齐,参考:C-基础知识-内存对齐
注意:
__attribute__
中指定 aligned(16)
仍然只能为您提供 8 字节对齐。所以具体信息就得查看链接器文档。__attribute__ packed
功能
__attrubte__ ((packed))
的作用就是告诉编译器取消结构体或者是联合体在编译过程中的优化对齐,按照实际占用字节数进行对齐。当附加到枚举定义时,它表示应该使用最小整型。示例1
struct S2 {
short b[3];
int a;
} __attribute__ ((aligned (8))); // 或者用:__attribute__ ((__aligned__(8)))
struct S3 {
short b[3];
int a;
} __attribute__ ((packed)); // 或者用:__attribute__ ((__packed__))
// 输出:
// struct S2 size: 16
// struct S3 size: 10
示例 /usr/include/net/ethernet.h
/* 10Mb/s ethernet header */
struct ether_header
{
uint8_t ether_dhost[ETH_ALEN]; /* destination eth addr */
uint8_t ether_shost[ETH_ALEN]; /* source ether addr */
uint16_t ether_type; /* packet type ID field */
} __attribute__ ((__packed__));
注意
您只能在枚举、结构或联合的定义上指定此属性,而不能在未定义枚举类型、结构或联合的 typedef
上指定。
内部有结构体成员时:
struct my_unpacked_struct
{
char c;
int i;
};
struct my_packed_struct __attribute__ ((__packed__))
{
char c;
int i;
struct my_unpacked_struct s;
};
上面的定义中: struct my_packed_struct 的每个成员间是没有空隙的,sizeof(struct my_packed_struct) = sizeof(char) + sizeof(int) + sizof(struct my_unpacked_struct)
但是对于成员 s 即 struct my_unpacked_struc 的内部,并没有压缩内存。如果要压缩,就得在定义 struct my_unpacked_struc 时使用 packed 属性。
当用到 typedef
时,要特别注意 __attribute__ ((packed))
放置的位置
typedef struct{
int a;
char b;
long c;
long double d;
} __attribute__ ((packed)) str_p_t1; // 有效 packed
typedef struct{
int a;
char b;
long c;
long double d;
}str_p_t2 __attribute__ ((packed)); // 无效 packed,大小为自动对齐时的大小,编译时会告警
printf("struct str_p_t1 size:%ld\n", sizeof(str_p_t1));
printf("struct str_p_t2 size:%ld\n", sizeof(str_p_t2));
// 输出结果:
// struct str_p_t1 size:29
// struct str_p_t2 size:32
GCC
特性一样,编译器 LLVM Clang
支持了 __attribute__
, 还加入了一小部分扩展特性。要检查能否使用特定的属性,可以用 __has_attribute
这个指令。https://zhuanlan.zhihu.com/p/140462815
关于 LLVM 和 Clang
LLVM只是一个编译器框架,所以还需要一个前端来支撑整个系统,所以 Apple 又拨款拨人一起研发了Clang,作为整个编译器的前端,Clang用来编译C、C++和Objective-C。
最初时,LLVM的前端是GCC,后来 Apple 还是立志自己开发了一套 Clang 出来把 GCC 取代了,不过现在带有 Dragon Egg 的 GCC 还是可以生成 LLVM IR,也同样可以取代 Clang 的功能,
我们也可以开发自己的前端,和 LLVM 后端配合起来,实现我们自定义的编程语言的编译器。
原文:https://www.cnblogs.com/9stars/p/14756718.html