4.3.1.7.5.6.5.2. 处理format属性
对于printf,scanf等类型的函数,真正的检查要在调用处进行。在其声明中,只进行少许检查,由handle_format_attribute 执行。现在参数args是节点ATTR_PRINTF_1_0,而传入的no_add_attrs是0。
2741 tree
2742 handle_format_attribute (tree *node, tree name ATTRIBUTE_UNUSED, in c-format.c
2743 tree args, int flags, bool *no_add_attrs)
2744 {
2745 tree type = *node;
2746 function_format_info info;
2747 tree argument;
2748
2749 if (!decode_format_attr (args, &info, 0))
2750 {
2751 *no_add_attrs = true;
2752 return NULL_TREE;
2753 }
第一步是验证属性ATTR_PRINTF_1_0。工作很简单——检查要求的格式是有效的,所带的实参是合理的。编译器定义了下面的类型来携带所需要的信息。
58 enum format_type { printf_format_type, asm_fprintf_format_type, in c-format.c
59 gcc_diag_format_type, gcc_cdiag_format_type,
60 gcc_cxxdiag_format_type,
61 scanf_format_type, strftime_format_type,
62 strfmon_format_type, format_type_error };
64 typedef struct function_format_info
65 {
66 enum format_type format_type; /* type of format (printf, scanf, etc.) */
67 unsigned HOST_WIDE_INT format_num; /* number of format argument */
68 unsigned HOST_WIDE_INT first_arg_num; /* number of first arg (zero for varargs) */
69 } function_format_info;
178 static bool
179 decode_format_attr (tree args, function_format_info *info, int validated_p) in c-format.c
180 {
181 tree format_type_id = TREE_VALUE (args);
182 tree format_num_expr = TREE_VALUE (TREE_CHAIN (args));
183 tree first_arg_num_expr
184 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
185
186 if (TREE_CODE (format_type_id) != IDENTIFIER_NODE)
187 {
188 if (validated_p)
189 abort ();
190 error ("unrecognized format specifier");
191 return false;
192 }
193 else
194 {
195 const char *p = IDENTIFIER_POINTER (format_type_id);
196
197 info->format_type = decode_format_type (p);
198
199 if (info->format_type == format_type_error)
200 {
201 if (validated_p)
202 abort ();
203 warning ("`%s' is an unrecognized format function type", p);
204 return false;
205 }
206 }
验证格式的类型简单明了。为了保存所有格式类型的信息,GCC维护了一个全局数组format_types,该数组以format_type为索引。在format_types中找出格式所对应的名字就能得到答案。
1036 static enum format_type
1037 decode_format_type (const char *s) in c-format.c
1038 {
1039 int i;
1040 int slen;
1041 slen = strlen (s);
1042 for (i = 0; i < (int) format_type_error; i++)
1043 {
1044 int alen;
1045 if (!strcmp (s, format_types[i].name))
1046 break;
1047 alen = strlen (format_types[i].name);
1048 if (slen == alen + 4 && s[0] == '_' && s[1] == '_'
1049 && s[slen - 1] == '_' && s[slen - 2] == '_'
1050 && !strncmp (s + 2, format_types[i].name, alen))
1051 break;
1052 }
1053 return ((enum format_type) i);
1054 }
而对于格式属性的参数的要求是,它们必须是常量,而且格式字符串必须出现在要被格式的参数之前(如果有的话,记住0表示不使用)。
decode_format_attr (continue)
208 if (!get_constant (format_num_expr, &info->format_num, validated_p))
209 {
210 error ("format string has invalid operand number");
211 return false;
212 }
213
214 if (!get_constant (first_arg_num_expr, &info->first_arg_num, validated_p))
215 {
216 error ("'...' has invalid operand number");
217 return false;
218 }
219
220 if (info->first_arg_num != 0 && info->first_arg_num <= info->format_num)
221 {
222 if (validated_p)
223 abort ();
224 error ("format string arg follows the args to be formatted");
225 return false;
226 }
227
228 return true;
229 }
现在下面的type是builtin_types [BT_FN_INT_CONST_STRING_VALIST_ARG]——内建函数“__builtin_vprintf”的类型。对于对应格式字符串的参数,它应该是const_string_type_node,这个类型在c_common_nodes_and_builtins中创建,是代表“const char*”的节点。
handle_format_attribute (continue)
2755 argument = TYPE_ARG_TYPES (type);
2756 if (argument)
2757 {