从这里开始,我们将多次看到FUNCTION_DECL及FUNCTION_TYPE。正如我们所了解的,在C/C++中,函数具有类型。例如,“int a (int A);”及“int b (int B);”,这2个函数具有相同的类型(它们都可以被指针类型“int(*)(int)”所指向)。但它们是同一类型的2个不同的声明,它们的行为可以完全相异。你可以说类型有点类似接口,而声明则有些像接口下不同的实现。
在后面,我们将看到一些属性可以应用于类型,而另一些则应用于声明。这个区别即来自于类型与声明的不同之处。记住这一点。
这里第一个序列的宏为builtin-types.def中的声明创建FUNCTION_TYPE节点。看到所创建的节点保存在builtin_types中所对应的槽中。 而且对于FUNCTION_TYPE,如果参数值链表的最后一个节点不是void_list_node,那么这个函数类型不接受可变数目参数。否则,则为接受可变数目参数。
c_common_nodes_and_builtins (continue)
3336 #define DEF_PRIMITIVE_TYPE(ENUM, VALUE) /
3337 builtin_types[(int) ENUM] = VALUE;
3338 #define DEF_FUNCTION_TYPE_0(ENUM, RETURN) /
3339 builtin_types[(int) ENUM] /
3340 = build_function_type (builtin_types[(int) RETURN], /
3341 void_list_node);
3342 #define DEF_FUNCTION_TYPE_1(ENUM, RETURN, ARG1) /
3343 builtin_types[(int) ENUM] /
3344 = build_function_type (builtin_types[(int) RETURN], /
3345 tree_cons (NULL_TREE, /
3346 builtin_types[(int) ARG1], /
3347 void_list_node));
3348 #define DEF_FUNCTION_TYPE_2(ENUM, RETURN, ARG1, ARG2) /
3349 builtin_types[(int) ENUM] /
3350 = build_function_type /
3351 (builtin_types[(int) RETURN], /
3352 tree_cons (NULL_TREE, /
3353 builtin_types[(int) ARG1], /
3354 tree_cons (NULL_TREE, /
3355 builtin_types[(int) ARG2], /
3356 void_list_node)));
3357 #define DEF_FUNCTION_TYPE_3(ENUM, RETURN, ARG1, ARG2, ARG3) /
3358 builtin_types[(int) ENUM] /
3359 = build_function_type /
3360 (builtin_types[(int) RETURN], /
3361 tree_cons (NULL_TREE, /
3362 builtin_types[(int) ARG1], /
3363 tree_cons (NULL_TREE, /
3364 builtin_types[(int) ARG2], /
3365 tree_cons (NULL_TREE, /
3366 builtin_types[(int) ARG3], /
3367 void_list_node))));
3368 #define DEF_FUNCTION_TYPE_4(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4) /
3369 builtin_types[(int) ENUM] /
3370 = build_function_type /
3371 (builtin_types[(int) RETURN], /
3372 tree_cons (NULL_TREE, /
3373 builtin_types[(int) ARG1], /
3374 tree_cons (NULL_TREE, /
3375 builtin_types[(int) ARG2], /
3376 tree_cons /
3377 (NULL_TREE, /
3378 builtin_types[(int) ARG3], /
3379 tree_cons (NULL_TREE, /
3380 builtin_types[(int) ARG4], /
3381 void_list_node)))));
3382 #define DEF_FUNCTION_TYPE_VAR_0(ENUM, RETURN) /
3383 builtin_types[(int) ENUM] /
3384 = build_function_type (builtin_types[(int) RETURN], NULL_TREE);
3385 #define DEF_FUNCTION_TYPE_VAR_1(ENUM, RETURN, ARG1) /
3386 builtin_types[(int) ENUM] /
3387 = build_function_type (builtin_types[(int) RETURN], /
3388 tree_cons (NULL_TREE, /
3389 builtin_types[(int) ARG1], /
3390 NULL_TREE));
3391
3392 #define DEF_FUNCTION_TYPE_VAR_2(ENUM, RETURN, ARG1, ARG2) /
3393 builtin_types[(int) ENUM] /
3394 = build_function_type /
3395 (builtin_types[(int) RETURN], /
3396 tree_cons (NULL_TREE, /
3397 builtin_types[(int) ARG1], /
3398 tree_cons (NULL_TREE, /
3399 builtin_types[(int) ARG2], /
3400 NULL_TREE)));
3401
3402 #define DEF_FUNCTION_TYPE_VAR_3(ENUM, RETURN, ARG1, ARG2, ARG3) /
3403 builtin_types[(int) ENUM] /
3404 = build_function_type /
3405 (builtin_types[(int) RETURN], /
3406 tree_cons (NULL_TREE, /
3407 builtin_types[(int) ARG1], /
3408 tree_cons (NULL_TREE, /
3409 builtin_types[(int) ARG2], /
3410 tree_cons (NULL_TREE, /
3411 builtin_types[(int) ARG3], /
3412 NULL_TREE))));
3413
3414 #define DEF_POINTER_TYPE(ENUM, TYPE) /
3415 builtin_types[(int) ENUM] /
3416 = build_function_type (builtin_types[(int) TYPE]);
3417 #include "builtin-types.def"
3418 #undef DEF_PRIMITIVE_TYPE
3419 #undef DEF_FUNCTION_TYPE_1
3420 #undef DEF_FUNCTION_TYPE_2
3421 #undef DEF_FUNCTION_TYPE_3
3422 #undef DEF_FUNCTION_TYPE_4
3423 #undef DEF_FUNCTION_TYPE_VAR_0
3424 #undef DEF_FUNCTION_TYPE_VAR_1
3425 #undef DEF_FUNCTION_TYPE_VAR_2
3426 #undef DEF_FUNCTION_TYPE_VAR_3
3427 #undef DEF_POINTER_TYPE
在GNU C中,申明在程序中被调用函数的某些状况,将帮助编译器优化函数调用并执行更仔细的检查。
关键字__attribute__允许在声明时指明特定的属性。这个关键字后跟着在双括号内的属性规范。目前在所有平台上都能用于函数的属性有:aligned,alloc_size,noreturn,returns_twice,noinline,always_inline,flatten,pure,const,nothrow,sentinel,format,format_arg,no_instrument_function,section,constructor,destructor,used,unused,deprecated,weak,malloc,alias,warn_unused_result,nonnull,gnu_inline,externally_visible,hot,cold,artificial,error及warning。
由GCC提供的内建函数,在编译器内部看来,与其他普通的函数没有什么区别,它们也需要某些属性虽然不是全部。和声明的其他部分相同,当在编译器内部进行处理时,声明的属性也应该是树节点的形式。因此在创建内建函数的树节点前,属性的节点先要准备好。现在我们将要创建它们。
c_common_nodes_and_builtins (continue)
3429 c_init_attributes ();
下面的built_in_attributes是由built_in_attribute索引的标识符数组。built_in_attribute有如下定义:
3036 enum built_in_attribute in c-common.c
3037 {
3038 #define DEF_ATTR_NULL_TREE(ENUM) ENUM,
3039 #define DEF_ATTR_INT(ENUM, VALUE) ENUM,
3040 #define DEF_ATTR_IDENT(ENUM, STRING) ENUM,
3041 #define DEF_ATTR_TREE_LIST(ENUM, PURPOSE, VALUE, CHAIN) ENUM,
3042 #include "builtin-attrs.def"
3043 #undef DEF_ATTR_NULL_TREE
3044 #undef DEF_ATTR_INT
3045 #undef DEF_ATTR_IDENT
3046 #undef DEF_ATTR_TREE_LIST
3047 ATTR_LAST
3048 };
然后在c_init_attributes中,这些宏被重新定义为:
DEF_ATTR_NULL_TREE (ENUM)构建一个NULL_TREE节点。
DEF_ATTR_INT (ENUM, VALUE)构建一个INTEGER_CST节点,具有值VALUE(以HOST_WIDE_INT表示的整数)。
DEF_ATTR_IDENT (ENUM, STRING)构建为STRING一个IDENTIFIER_NODE节点。
DEF_ATTR_TREE_LIST (ENUM, PURPOSE, VALUE, CHAIN)构建一个TREE_LIST 节点,具有给定的PURPOSE,VALUE及CHAIN内容。
4258 static void
4259 c_init_attributes (void) in c-common.c
4260 {
4261 /* Fill in the built_in_attributes array. */
4262 #define DEF_ATTR_NULL_TREE(ENUM) /
4263 built_in_attributes[(int) ENUM] = NULL_TREE;
4264 #define DEF_ATTR_INT(ENUM, VALUE) /
4265 built_in_attributes[(int) ENUM] = build_int_2 (VALUE, VALUE < 0 ? -1 : 0);
4266 #define DEF_ATTR_IDENT(ENUM, STRING) /
4267 built_in_attributes[(int) ENUM] = get_identifier (STRING);
4268 #define DEF_ATTR_TREE_LIST(ENUM, PURPOSE, VALUE, CHAIN) /
4269 built_in_attributes[(int) ENUM] /
4270 = tree_cons (built_in_attributes[(int) PURPOSE], /
4271 built_in_attributes[(int) VALUE], /
4272 built_in_attributes[(int) CHAIN]);
4273 #include "builtin-attrs.def"
4274 #undef DEF_ATTR_NULL_TREE
4275 #undef DEF_ATTR_INT
4276 #undef DEF_ATTR_IDENT
4277 #undef DEF_ATTR_TREE_LIST
4278 }
这些宏看上去让人有点头晕,最好让GCC为我们来展开这些宏。展开的结果如下。这些代码为内建函数产生属性。
built_in_attributes[(int) ATTR_0] = build_int_2 (0, 0 < 0 ? -1 : 0);
built_in_attributes[(int) ATTR_LIST_0] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_0], built_in_attributes[(int) ATTR_NULL]);
built_in_attributes[(int) ATTR_1] = build_int_2 (1, 1 < 0 ? -1 : 0);
built_in_attributes[(int) ATTR_LIST_1] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_1], built_in_attributes[(int) ATTR_NULL]);
built_in_attributes[(int) ATTR_2] = build_int_2 (2, 2 < 0 ? -1 : 0);
built_in_attributes[(int) ATTR_LIST_2] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_2], built_in_attributes[(int) ATTR_NULL]);
built_in_attributes[(int) ATTR_3] = build_int_2 (3, 3 < 0 ? -1 : 0);
built_in_attributes[(int) ATTR_LIST_3] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_3], built_in_attributes[(int) ATTR_NULL]);
built_in_attributes[(int) ATTR_4] = build_int_2 (4, 4 < 0 ? -1 : 0);
built_in_attributes[(int) ATTR_LIST_4] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_4], built_in_attributes[(int) ATTR_NULL]);
built_in_attributes[(int) ATTR_LIST_1_0] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_1], built_in_attributes[(int) ATTR_LIST_0]);
built_in_attributes[(int) ATTR_LIST_1_2] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_1], built_in_attributes[(int) ATTR_LIST_2]);
built_in_attributes[(int) ATTR_LIST_2_0] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_2], built_in_attributes[(int) ATTR_LIST_0]);
built_in_attributes[(int) ATTR_LIST_2_3] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_2], built_in_attributes[(int) ATTR_LIST_3]);
built_in_attributes[(int) ATTR_LIST_3_0] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_3], built_in_attributes[(int) ATTR_LIST_0]);
built_in_attributes[(int) ATTR_LIST_3_4] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_3], built_in_attributes[(int) ATTR_LIST_4]);
built_in_attributes[(int) ATTR_CONST] = get_identifier ("const"); built_in_attributes[(int) ATTR_FORMAT] = get_identifier ("format"); built_in_attributes[(int) ATTR_FORMAT_ARG] = get_identifier ("format_arg"); built_in_attributes[(int) ATTR_MALLOC] = get_identifier ("malloc"); built_in_attributes[(int) ATTR_NONNULL] = get_identifier ("nonnull"); built_in_attributes[(int) ATTR_NORETURN] = get_identifier ("noreturn"); built_in_attributes[(int) ATTR_NOTHROW] = get_identifier ("nothrow"); built_in_attributes[(int) ATTR_PRINTF] = get_identifier ("printf"); built_in_attributes[(int) ATTR_ASM_FPRINTF] = get_identifier ("asm_fprintf"); built_in_attributes[(int) ATTR_GCC_DIAG] = get_identifier ("gcc_diag"); built_in_attributes[(int) ATTR_GCC_CDIAG] = get_identifier ("gcc_cdiag"); built_in_attributes[(int) ATTR_GCC_CXXDIAG] = get_identifier ("gcc_cxxdiag"); built_in_attributes[(int) ATTR_PURE] = get_identifier ("pure"); built_in_attributes[(int) ATTR_SCANF] = get_identifier ("scanf"); built_in_attributes[(int) ATTR_STRFMON] = get_identifier ("strfmon"); built_in_attributes[(int) ATTR_STRFTIME] = get_identifier ("strftime");
built_in_attributes[(int) ATTR_NOTHROW_LIST] = tree_cons (built_in_attributes[(int) ATTR_NOTHROW], built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_NULL]);
built_in_attributes[(int) ATTR_CONST_NOTHROW_LIST] = tree_cons (built_in_attributes[(int) ATTR_CONST], built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_NOTHROW_LIST]);
built_in_attributes[(int) ATTR_PURE_NOTHROW_LIST] = tree_cons (built_in_attributes[(int) ATTR_PURE], built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_NOTHROW_LIST]);
built_in_attributes[(int) ATTR_NORETURN_NOTHROW_LIST] = tree_cons (built_in_attributes[(int) ATTR_NORETURN], built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_NOTHROW_LIST]);
built_in_attributes[(int) ATTR_MALLOC_NOTHROW_LIST] = tree_cons (built_in_attributes[(int) ATTR_MALLOC], built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_NOTHROW_LIST]);
built_in_attributes[(int) ATTR_NOTHROW_NONNULL_1] = tree_cons (built_in_attributes[(int) ATTR_NONNULL], built_in_attributes[(int) ATTR_LIST_1], built_in_attributes[(int) ATTR_NOTHROW_LIST]);
built_in_attributes[(int) ATTR_NOTHROW_NONNULL_2] = tree_cons (built_in_attributes[(int) ATTR_NONNULL], built_in_attributes[(int) ATTR_LIST_2], built_in_attributes[(int) ATTR_NOTHROW_LIST]);
built_in_attributes[(int) ATTR_NOTHROW_NONNULL_3] = tree_cons (built_in_attributes[(int) ATTR_NONNULL], built_in_attributes[(int) ATTR_LIST_3], built_in_attributes[(int) ATTR_NOTHROW_LIST]);
built_in_attributes[(int) ATTR_NOTHROW_NONNULL_1_2] = tree_cons (built_in_attributes[(int) ATTR_NONNULL], built_in_attributes[(int) ATTR_LIST_2], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_1]);
built_in_attributes[(int) ATTR_NOTHROW_NONNULL_1_4] = tree_cons (built_in_attributes[(int) ATTR_NONNULL], built_in_attributes[(int) ATTR_LIST_4], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_1]);
built_in_attributes[(int) ATTR_CONST_NOTHROW_NONNULL_1] = tree_cons (built_in_attributes[(int) ATTR_CONST], built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_1]);
built_in_attributes[(int) ATTR_PURE_NOTHROW_NONNULL_1] = tree_cons (built_in_attributes[(int) ATTR_PURE], built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_1]);
built_in_attributes[(int) ATTR_PURE_NOTHROW_NONNULL_1_2] = tree_cons (built_in_attributes[(int) ATTR_PURE], built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_1_2]);
built_in_attributes[(int) ATTR_MALLOC_NOTHROW_NONNULL_1] = tree_cons (built_in_attributes[(int) ATTR_MALLOC], built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_1]);
built_in_attributes[(int) ATTR_PRINTF_1_0] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_PRINTF], built_in_attributes[(int) ATTR_LIST_1_0]);
built_in_attributes[(int) ATTR_FORMAT_PRINTF_1_0] = tree_cons (built_in_attributes[(int) ATTR_FORMAT], built_in_attributes[(int) ATTR_PRINTF_1_0], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_1]);
built_in_attributes[(int) ATTR_PRINTF_1_2] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_PRINTF], built_in_attributes[(int) ATTR_LIST_1_2]);
built_in_attributes[(int) ATTR_FORMAT_PRINTF_1_2] = tree_cons (built_in_attributes[(int) ATTR_FORMAT], built_in_attributes[(int) ATTR_PRINTF_1_2], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_1]);
built_in_attributes[(int) ATTR_PRINTF_2_0] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_PRINTF], built_in_attributes[(int) ATTR_LIST_2_0]);
built_in_attributes[(int) ATTR_FORMAT_PRINTF_2_0] = tree_cons (built_in_attributes[(int) ATTR_FORMAT], built_in_attributes[(int) ATTR_PRINTF_2_0], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_2]);
built_in_attributes[(int) ATTR_PRINTF_2_3] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_PRINTF], built_in_attributes[(int) ATTR_LIST_2_3]);
built_in_attributes[(int) ATTR_FORMAT_PRINTF_2_3] = tree_cons (built_in_attributes[(int) ATTR_FORMAT], built_in_attributes[(int) ATTR_PRINTF_2_3], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_2]);
built_in_attributes[(int) ATTR_PRINTF_3_0] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_PRINTF], built_in_attributes[(int) ATTR_LIST_3_0]);
built_in_attributes[(int) ATTR_FORMAT_PRINTF_3_0] = tree_cons (built_in_attributes[(int) ATTR_FORMAT], built_in_attributes[(int) ATTR_PRINTF_3_0], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_3]);
built_in_attributes[(int) ATTR_PRINTF_3_4] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_PRINTF], built_in_attributes[(int) ATTR_LIST_3_4]);
built_in_attributes[(int) ATTR_FORMAT_PRINTF_3_4] = tree_cons (built_in_attributes[(int) ATTR_FORMAT], built_in_attributes[(int) ATTR_PRINTF_3_4], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_3]);
built_in_attributes[(int) ATTR_SCANF_1_0] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_SCANF], built_in_attributes[(int) ATTR_LIST_1_0]);
built_in_attributes[(int) ATTR_FORMAT_SCANF_1_0] = tree_cons (built_in_attributes[(int) ATTR_FORMAT], built_in_attributes[(int) ATTR_SCANF_1_0], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_1]);
built_in_attributes[(int) ATTR_SCANF_1_2] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_SCANF], built_in_attributes[(int) ATTR_LIST_1_2]);
built_in_attributes[(int) ATTR_FORMAT_SCANF_1_2] = tree_cons (built_in_attributes[(int) ATTR_FORMAT], built_in_attributes[(int) ATTR_SCANF_1_2], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_1]);
built_in_attributes[(int) ATTR_SCANF_2_0] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_SCANF], built_in_attributes[(int) ATTR_LIST_2_0]);
built_in_attributes[(int) ATTR_FORMAT_SCANF_2_0] = tree_cons (built_in_attributes[(int) ATTR_FORMAT], built_in_attributes[(int) ATTR_SCANF_2_0], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_2]);
built_in_attributes[(int) ATTR_SCANF_2_3] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_SCANF], built_in_attributes[(int) ATTR_LIST_2_3]);
built_in_attributes[(int) ATTR_FORMAT_SCANF_2_3] = tree_cons (built_in_attributes[(int) ATTR_FORMAT], built_in_attributes[(int) ATTR_SCANF_2_3], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_2]);
built_in_attributes[(int) ATTR_STRFTIME_3_0] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_STRFTIME], built_in_attributes[(int) ATTR_LIST_3_0]);
built_in_attributes[(int) ATTR_FORMAT_STRFTIME_3_0] = tree_cons (built_in_attributes[(int) ATTR_FORMAT], built_in_attributes[(int) ATTR_STRFTIME_3_0], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_3]);
built_in_attributes[(int) ATTR_STRFMON_3_4] = tree_cons (built_in_attributes[(int) ATTR_NULL], built_in_attributes[(int) ATTR_STRFMON], built_in_attributes[(int) ATTR_LIST_3_4]);
built_in_attributes[(int) ATTR_FORMAT_STRFMON_3_4] = tree_cons (built_in_attributes[(int) ATTR_FORMAT], built_in_attributes[(int) ATTR_STRFMON_3_4], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_3]);
built_in_attributes[(int) ATTR_FORMAT_ARG_1] = tree_cons (built_in_attributes[(int) ATTR_FORMAT_ARG], built_in_attributes[(int) ATTR_LIST_1], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_1]);
built_in_attributes[(int) ATTR_FORMAT_ARG_2] = tree_cons (built_in_attributes[(int) ATTR_FORMAT_ARG], built_in_attributes[(int) ATTR_LIST_2], built_in_attributes[(int) ATTR_NOTHROW_NONNULL_2]); |
不过,仍然让人惊讶需要如此多的代码来产生这些ATTR_FORMAT_FUNC_NUM_NUM节点。为了更清楚地看到这些节点,我们在下图显示了其中的一部分,我们将与ATTR_FORMAT_PRINTF_1_0相关的节点标记为红色。
图37:内建函数属性
4.3.1.7.5.5.1. 内建函数属性的细节
为了更好地理解这些内建函数,从【6】摘录的,有文档记载的属性的,详细描述陈列如下:
format (archetype, string-index, first-to-check) 这个格式属性表示一个接受printf,scanf,strftime或strfmon形式参数的函数,这些参数应该按格式字符串进行类型检查。例如,声明: extern int my_printf (void *my_object, const char *my_format, ...) __attribute__ ((format (printf, 2, 3))); 将促使编译器检查调用my_printf的参数与printf形式的格式字符串参数my_format一致。 参数archetype确定如何解释格式字符串,它只能取printf,scanf,strftime或strfmon(你也可以使用__printf__,__scanf__,__strftime__或__strfmon__)。参数string-index 表明哪个参数是格式字符串(计数从1开始),而first-to-check是第一个按格式字符串进行检查的参数的序号。对于那些没有可检查参数的函数(例如vprintf(1)),则指定这个参数为0。在这种情况下,编译器只检查格式字符串的一致性。对于strftime格式,第三个参数要求必须为0。因为非静态的C++方法(method)有隐含的this指针参数,当确定string-index及 first-to-check的值时,这些方法的参数应该从2开始计算,而不是1。 在上面的例子中,格式字符串(my_format)是函数my_print的第二个参数,而需要检查的参数从第三个开始,因此格式属性正确的参数是2和3。 格式属性允许你指出自己的接受格式字符串作为参数的函数,因而GCC可以检查这些函数的调用中的错误。当要求给出相应的警告(使用-Wformat),编译器总是(除非使用了-ffreestanding或-fno-builtin)为标准库函数printf,fprintf,sprintf,scanf,fscanf,sscanf,strftime,vprintf,vfprintf及vsprintf检查格式,因此不需要改变头文件stdio.h。 在C99模式中,函数snprintf,vsnprintf,vscanf,vfscanf和vsscanf也都被检查。除了在严格符合C标准模式外,X/Open函数strfmon也象printf_unlocked及fprintf_unlocked那样检查。 目标平台可能会提供额外的格式类型的检查。
注(1):vprintf的声明是:int vprintf(const char *restrict format, va_list ap); |
format_arg (string-index) format_arg属性表示一个接受printf,scanf,strftime或strfmon风格的格式字符串,并修改它(例如,将它翻译至另一种语言)的函数。因此其结果能传递给printf,scanf,strftime或strfmon风格的函数(连同没转换的参数传给格式函数,这些参数与未修改字串中的相同)。例如,声明: extern char * my_dgettext (char *my_domain, const char *my_format) __attribute__ ((format_arg (2))); 促使编译器检查调用printf,scanf,strftime或strfmon类型函数的参数,而这些函数的格式字符串是对my_dgettext函数的调用,以使之与my_format格式字符串一致。如果format_arg属性没有被指定,编译器在这样对格式函数的调用中,所能告知将是格式字符串参数不是常量;当使用了-Wformat-nonliteral时,这将产生一个警告,不过没有这个属性,调用就不会被检查。 参数string-index指明哪个参数是格式字符串参数(从1开始计算)。因为非静态的C++方法(method)有隐含的this指针参数,类似方法的参数应该从2开始计算。format-arg 属性允许你指出自己的修改格式字符串的函数,因而GCC可以检查对printf,scanf,strftime或strfmon类型函数的调用,它们的实参则是对你函数的调用。编译器总是按这种方式处理gettext,dgettext及dcgettext,除非通过-ansi或合适的-std选项,或使用-ffreestanding或-fno-builtin来要求严格的ISO C支持。 |
malloc malloc属性用于告诉编译器一个函数应该这样处理,它所返回的非空指针不会是其他有效指针的别名。这通常会改进优化。具有这个特性的标准函数包括malloc和calloc。 类似realloc的函数也有这个特性,只要在函数返回非空值后,旧的指针不再被引用。 |
nonnull (arg-index, ...) nonnull属性指明某些函数参数是非空指针。例如,声明: extern void * my_memcpy (void *dest, const void *src, size_t len) __attribute__((nonnull (1, 2))); 促使编译器检查在调用my_memcpy中,参数dest及src都是非空的。如果编译器确认一个空指针被传给一个标记为非空的参数,并且启动了-Wnonnull选项,则给出一个警告。编译器也可能选择执行优化,基于了解特定的函数参数不会为空。 如果没有对nonnull属性给出参数索引链表,那么所有的指针参数都标记为非空。为了说明这一点,下面的声明与前一个例子是等同的: extern void * my_memcpy (void *dest, const void *src, size_t len) __attribute__((nonnull)); |
noreturn 少数标准库函数,例如abort及exit,不会返回。GCC自动地知道这一点。一些程序定义可它们自己的不会返回的函数。你可以声明它们为不返回的,以告诉编译器这个事实。例如, void fatal () __attribute__ ((noreturn)); void fatal (/* . . . */) { /* . . . */ /* Print error message. */ /* . . . */ exit (1); } 关键字noreturn告诉编译器假设fatal不会返回。那么可以不用考虑fatal返回的情况而优化之。这会生成稍微好一些的代码。更重要的,它有助于避免关于未初始化变量的,谬误的警告。 关键字noreturn不会影响异常路径的起效:一个标记为noreturn的函数通过抛出异常或调用longjmp依然可以返回至调用者。 不能假定由调用者函数保存的寄存器会在调用非返回函数前恢复。 一个非返回函数具有void以外的返回类型是不合理的。 属性noreturn在早于2.5版本的GCC中没有实现。一个替代的,能在当前及更旧版本工作的,声明一个函数不返回的,方式如下: typedef void voidfn (); volatile voidfn fatal; 但这个方式在GNU C++下不工作。 |
nothrow nothrow属性用于告诉编译器一个函数不会抛出异常。例如,在标准C库中的绝大部分函数可以保证不会抛出异常,除了qsort和 bsearch,它们接受函数指针作为参数。Nothrow属性在早于GCC 3.3的版本没有实现。 |
pure 许多函数除了返回值没有别的效应,而且它们的返回值依赖于参数及/或全局变量。这样的函数,像算术操作符那样,是公共子表达式消除及循环优化的理想对象。这些函数应该使用属性pure来声明。例如, int square (int) __attribute__ ((pure)); 表明调用这个假想的平方函数的次数少于程序所表示的次数,是安全的。 一些常见的pure函数的例子是strlen或memcmp。有趣的非-pure函数有带有无限循环的函数,或者函数依赖于内存或其他系统资源,它们在2个紧邻的调用间可能发生变化(例如多线程环境下的feof)。 属性pure在GCC早于2.96版本中没有实现。 |
const 许多函数除了参数不检查其他值,而且除了返回值它们没有其他效应。基本上这是比pure属性稍微严格一些的类别,因为函数不允许访问全局内存。 注意到一个具有指针参数,并且考查所指向数据的函数,不能被声明为const。类似的,一个调用非-const函数的函数,通常也不能是const。一个返回void的const函数是没有意义的。 属性const在GCC早于2.5的版本中没有实现。一个替代的,能在当前及更早版本中工作的,声明函数没有副作用的,方式如下: typedef int intfn (); extern const intfn square; 这个方式在GNU C++ 2.6.0版本后不能工作,因为语言指出const必须附属于返回值。 |