3.3.3.1.1. C++的选项
对于C,C++或者obj-C,lang_hooks的handle_options都是c_common_handle_option。下面的OPT_*用作cl_options数组的索引。
251 int
252 c_common_handle_option (size_t scode, const char *arg, int value) in c-opts.c
253 {
254 const struct cl_option *option = &cl_options[scode];
255 enum opt_code code = (enum opt_code) scode;
256 int result = 1;
257
258 switch (code)
259 {
260 default:
261 result = permit_fortran_options;
262 break;
263
264 case OPT__output_pch_:
265 pch_file = arg;
266 break;
267
268 case OPT_A:
269 defer_opt (code, arg);
270 break;
271
272 case OPT_C:
273 cpp_opts->discard_comments = 0;
274 break;
275
276 case OPT_CC:
277 cpp_opts->discard_comments = 0;
278 cpp_opts->discard_comments_in_macro_exp = 0;
279 break;
280
281 case OPT_D:
282 defer_opt (code, arg);
283 break;
284
285 case OPT_E:
286 flag_preprocess_only = 1;
287 break;
288
289 case OPT_H:
290 cpp_opts->print_include_names = 1;
291 break;
292
293 case OPT_I:
294 if (strcmp (arg, "-"))
295 add_path (xstrdup (arg), BRACKET, 0);
296 else
297 {
298 if (quote_chain_split)
299 error ("-I- specified twice");
300 quote_chain_split = true;
301 split_quote_chain ();
302 }
303 break;
304
305 case OPT_M:
306 case OPT_MM:
307 /* When doing dependencies with -M or -MM, suppress normal
308 preprocessed output, but still do -dM etc. as software
309 depends on this. Preprocessed output does occur if -MD, -MMD
310 or environment var dependency generation is used. */
311 cpp_opts->deps.style = (code == OPT_M ? DEPS_SYSTEM: DEPS_USER);
312 flag_no_output = 1;
313 cpp_opts->inhibit_warnings = 1;
314 break;
315
316 case OPT_MD:
317 case OPT_MMD:
318 cpp_opts->deps.style = (code == OPT_MD ? DEPS_SYSTEM: DEPS_USER);
319 deps_file = arg;
320 break;
321
322 case OPT_MF:
323 deps_seen = true;
324 deps_file = arg;
325 break;
在265行,pch_file表示我们要作为预编译头文件的文件名,或者为NULL如果不需要产生预编译头文件。
在268行,-Aquestion[=answer]指定了,对应于具有以下形式的断言的,回答(answer):
#if #question(answer)
也可以使用–assert代替–A,作为这个选项。
而在281行,-Dmacro[=string],如果string被指定,具有`macro`名字的宏被定义,就好像在代码中被定义那样。例如,-Dbrunt=logger将产生如下定义:
#define brunt=logger
如果string没有指定,string被认为是“1”。例如,-Dminke将产生如下定义:
#define minke 1
所有的-D选项都在任一一个-U选项前得到处理,而所有-U选项在任一一个-include或者-imacros选项前处理。
选项–A和–D,现阶段都由defer_opt处理,注意到arg指向-A或-D后的字符串。
179 static void
180 defer_opt (enum opt_code code, const char *arg) in c-opts.c
181 {
182 deferred_opts[deferred_count].code = code;
183 deferred_opts[deferred_count].arg = arg;
184 deferred_count++;
185 }
deferred_opts保存由c_common_handle_option解析的选项,但它们的处理要推迟到函数c_common_post_options中。
如果flag_preprocess_only(–E)成立,编译器在对源代码进行预处理后,就会停下并输出结果。这个结果会由标准输出输出,除非使用了-o选项指定了输出文件。那些不需要预处理的输入文件将被忽略。而是否需要处理则要么由文件名后缀,或者由-x选项指明语言类型,来确定。这个选项定义环境变量__GNUC__,__GNUC_MINOR__及__GNUC_PATCHLEVEL__。
选项–H输出所有被使用头文件的嵌套链,及缺失多次包含防卫的头文件列表。这个选项也可写作--trace-includes。
对于选项–I path,则调用add_path来处理这个参数。这个包含路径的信息由如下定义的cpp_dir结构来保存。
402 struct cpp_dir in cpplib.h
403 {
404 /* NULL-terminated singly-linked list. */
405 struct cpp_dir *next;
406
407 /* NAME of the directory, NUL-terminated. */
408 char *name;
409 unsigned int len;
410
411 /* One if a system header, two if a system header that has extern
412 "C" guards for C++. */
413 unsigned char sysp;
414
415 /* Mapping of file names for this directory for MS-DOS and related
416 platforms. A NULL-terminated array of (from, to) pairs. */
417 const char **name_map;
418
419 /* The C front end uses these to recognize duplicated
420 directories in the search path. */
421 ino_t ino;
422 dev_t dev;
423 };
在系统中,被包含的头文件可分为4类。其一是由“”包含,而另一种由<>包含,再者表示为系统头文件,最后则由选项-idirafter引入。因此在下面的代码中,heads和tails都是大小为4的数组。每一对通过分别指向头文件链表的头和尾,维护着一类头文件的路径。
303 void
304 add_path (char *path, int chain, int cxx_aware) in c-inpath.c
305 {
306 struct cpp_dir *p;
307
308 #if defined (HAVE_DOS_BASED_FILE_SYSTEM)
309 /* Convert all backslashes to slashes. The native CRT stat()
310 function does not recognise a directory that ends in a backslash
311 (unless it is a drive root dir, such "c:/"). Forward slashes,
312 trailing or otherwise, cause no problems for stat(). */
313 char* c;
314 for (c = path; *c; c++)
315 if (*c == '//') *c = '/';
316 #endif
317
318 p = xmalloc (sizeof (struct cpp_dir));
319 p->next = NULL;
320 p->name = path;
321 if (chain == SYSTEM || chain == AFTER)
322 p->sysp = 1 + !cxx_aware;
323 else
324 p->sysp = 0;
325
326 if (tails[chain])
327 tails[chain]->next = p;
328 else
329 heads[chain] = p;
330 tails[chain] = p;
331 }
上面的321行,在这里cxx_aware是0,因此由-I,-dirafter及-isystem引入的查找路径皆认为其中包含的头文件,若要用于C++,需要extern “C”保护字段。而选项-I-表示不能在当前目录中查找源文件,编译命令中指定的除外。因此调用split_quote_chain来移除被“”引入的路径。注意到这里的代码有内存泄露的问题。
290 void
291 split_quote_chain (void) in c-inpath.c
292 {
293 heads[QUOTE] = heads[BRACKET];
294 tails[QUOTE] = tails[BRACKET];
295 heads[BRACKET] = NULL;
296 tails[BRACKET] = NULL;
297 /* This is NOT redundant. */
298 quote_ignores_source_dir = true;
299 }
上面c_common_handle_option的305行,通过选项-M,预处理器会输出能用于makefile的规则。这个规则包括了目标文件名,冒号,源文件名及所包含的文件名。这些包含的文件名以全路径名分行列出。如果在命令行中有选项–include或-imacros,这些选项引入的文件名也被列出。选项-M隐含选项-E。
产生的规则包含目标文件名及依赖列表(the list of dependencies),但不包括编译源文件的规则。除非使用选项-MT或-MQ来指明名字,规则中的目标文件名与输入源文件名相同,只是更换了后缀。
其他涉及生成makefile规则的预处理选项有-MD,-MMD,-MF,-MG,-MM,-MP,-MQ及-MT。
而在311行,-MM类似于–M,只是不列出系统头文件。
对于选项–MG,它与-M或-MM连用表示,缺失的头文件可被视为需要生成的文件,这些文件与源文件在同一目录下。而产生的依赖列表则假定这些头文件都存在而且不包含其他头文件。
选项–MP,与-M或-MM连用,将为每个包含的文件产生一个假目标(dummy target)。这样做的唯一目的是,当移除头文件而没更新makefile时,防止make产生错误消息(头文件会由假目标生成)。
接下来在323行,deps_seen表示是否遇到了-MF选项。–MF filename,当与-M,-MM,-MD,或-MMD连用,这个选项指定了要写入依赖关系的文件名。另一个指定这个输出文件名的方法是设定环境变量DEPENDENCIES_OUTPUT。
另在337行,选项-MQ target类似于-MT,除了目标名,因为makefile的缘故,被适当地引用起来(quoted appropriately)。例如,命令gcc -M –MQ '$(OBJMRK)mrk.o' brink.c将产生如下规则:
$$(OBJMRK)mrk.o: brink.c
而–MT target,与-M或-MM连用,则指明生成的makefile规则中的目标文件名。默认的,目标文件名与输入源文件名一致,并具有后缀.o。选项-MT可以用来指定一个不同的名字,一个全路径名或者基于一个环境变量的名字。例如,命令gcc -M -MT '$(OBJMRK)mrk.o' brink.c将产生如下规则:
$(OBJMRK)mrk.o: brink.c
flag_no_line_commands(-P)如果非0,在使用-E选项时,预处理器不产生#line指示(directives)。
在348行,flag_working_directory(-fworking-directory)如果非0,表示我们不希望预处理器在当前工作目录下产生line指示(directives);如果是1,则表示我们希望它如此;而-1则表示我们需要根据是否产生了调试信息来决定(默认值为-1)。
对于–Umacro,如果macro之前已被定义,该宏的定义被移除。所有的-D选项都在-U选项前被处理,而所有的-U选项则在-include或-imacros选项前得到处理。这个选项的处理被推迟到c_common_post_options。
在356行,warn_abi(-Wabi)如果非0,表示警告那些由符合ABI的编译器编译时,会发生改变的部分(默认值为0)。【6】给出了G++不符合ABI的地方,但以我所测试的结果,这些不符合的地方,在当前版本已经得到修正。
c_common_handle_option (continue)
327 case OPT_MG:
328 deps_seen = true;
329 cpp_opts->deps.missing_files = true;
330 break;
331
332 case OPT_MP:
333 deps_seen = true;
334 cpp_opts->deps.phony_targets = true;
335 break;
336
337 case OPT_MQ:
338 case OPT_MT:
339 deps_seen = true;
340 defer_opt (code, arg);
341 break;
342
343 case OPT_P:
344 flag_no_line_commands = 1;
345 break;
346
347 case OPT_fworking_directory:
348 flag_working_directory = value;
349 break;
350
351 case OPT_U:
352 defer_opt (code, arg);
353 break;
354
355 case OPT_Wabi:
356 warn_abi = value;
357 break;
358
359 case OPT_Wall:
360 set_Wunused (value);
361 set_Wformat (value);
362 set_Wimplicit (value);
363 warn_char_subscripts = value;
364 warn_missing_braces = value;
365 warn_parentheses = value;
366 warn_return_type = value;
367 warn_sequence_point = value; /* Was C only. */
368 if (c_dialect_cxx ())
369 warn_sign_compare = value;
370 warn_switch = value;
371 warn_strict_aliasing = value;
372
373 /* Only warn about unknown pragmas that are not in system
374 headers. */
375 warn_unknown_pragmas = value;
376
377 /* We save the value of warn_uninitialized, since if they put
378 -Wuninitialized on the command line, we need to generate a
379 warning about not using it without also specifying -O. */
380 if (warn_uninitialized != 1)
381 warn_uninitialized = (value ? 2 : 0);
382
383 if (!c_dialect_cxx ())
384 /* We set this to 2 here, but 1 in -Wmain, so -ffreestanding
385 can turn it off only if it's not explicit. */
386 warn_main = value * 2;
387 else
388 {
389 /* C++-specific warnings. */
390 warn_nonvdtor = value;
391 warn_reorder = value;
392 warn_nontemplate_friend = value;
393 }
394
395 cpp_opts->warn_trigraphs = value;
396 cpp_opts->warn_comments = value;
397 cpp_opts->warn_num_sign_change = value;
398 cpp_opts->warn_multichar = value; /* Was C++ only. */
399 break;
对于–Wall选项,注意到360行的value表示选项是否为否定义(0为否定义,1为其他,见handle_option)。在这里value是1。
1572 void
1573 set_Wunused (int setting) in opts.c
1574 {
1575 warn_unused_function = setting;
1576 warn_unused_label = setting;
1577 /* Unused function parameter warnings are reported when either
1578 ``-Wextra -Wunused'' or ``-Wunused-parameter'' is specified.
1579 Thus, if -Wextra has already been seen, set warn_unused_parameter;
1580 otherwise set maybe_warn_extra_parameter, which will be picked up
1581 by set_Wextra. */
1582 maybe_warn_unused_parameter = setting;
1583 warn_unused_parameter = (setting && extra_warnings);
1584 warn_unused_variable = setting;
1585 warn_unused_value = setting;
1586 }
因此上面,所有关于警告未使用项目的标识都被设为真。在1583行,extra_warnings由选项–W/-Wextra设置。
36 void
37 set_Wformat (int setting) in c-format.c
38 {
39 warn_format = setting;
40 warn_format_extra_args = setting;
41 warn_format_zero_length = setting;
42 if (setting != 1)
43 {
44 warn_format_nonliteral = setting;
45 warn_format_security = setting;
46 warn_format_y2k = setting;
47 }
48 /* Make sure not to disable -Wnonnull if -Wformat=0 is specified. */
49 if (setting)
50 warn_nonnull = setting;
51 }
上面,warn_format(-Wformat)如果非0,在调用格式化I/O函数(printf,scanf,strftime,strfmon)时,警告异常的格式/参数对。
warn_format_extra_args(C,C++,ObjC,-Wformat-extra-args)如果非0,警告格式中多余的参数。
warn_format_zero_length(-Wformat-zero-length)如果非0而且也指定了-Wformat,警告长度为0的格式字符串。
warn_format_nonliteral(C,C++,ObjC,-Wformat-nonliteral)如果非0,警告非文字(non-literal)的格式参数(format arguments)。
warn_format_security(C,C++,ObjC,-Wformat-security)如果非0而且也指定了-Wformat,警告对诸如printf及scanf的调用所存在的安全问题。把变量而不是字符串常量用于格式的函数调用是不安全的,因为它可能包含%n。
warn_format_y2k(C,C++,ObjC,-Wformat-y2k)如果非0,警告strftime格式中的Y2K 问题。
warn_nonnull如果分0,警告把NULL传给标记为要求非NULL的参数。
1559 static void
1560 set_Wimplicit (int on) in c-opts.c
1561 {
1562 warn_implicit = on;
1563 warn_implicit_int = on;
1564 if (on)
1565 {
1566 if (mesg_implicit_function_declaration != 2)
1567 mesg_implicit_function_declaration = 1;
1568 }
1569 else
1570 mesg_implicit_function_declaration = 0;
1571 }
上面,warn_implicit如果非0,警告隐式的声明。
warn_implicit_int(C,-Wimplicit-int)如果非0,警告使用隐式声明的int。这个选项也可由-Wimplicit及-Wall设置。
mesg_implicit_function_declaration(C,-Wimplicit-function-declaration)如果非0,表示对使用隐式函数声明的给出消息;为1时表示给出警告,为2时给出错误。这个选项也可由-Wimplicit及-Wall设置。
在c_common_handle_option的363行,warn_char_subscript(C,C++,ObjC,-Wchar-subscripts)如果非0,警告下标具有char类型。因为char经常默认为有符号,这可能会导致错误。
warn_missing_braces(C,C++,ObjC,-Wmissing-braces,也由-Wall设置)如果非0且一个数组的初始化数值没有完全括起来时,给出警告。在下面的例子中,数组a和b都正确初始化了,但b所用的大括号更具体地指明了值的去处:
int a[2][2] = { 0, 1, 2, 3 };
int b[2][2] = { { 1, 2 }, { 3, 4} };
warn_parentheses(C,C++,ObjC,-Wparentheses,也由-Wall设置)如果非0,警告那些虽然语法上是正确的,但由于操作符的优先级或代码的结构,可能给编程的人带来混乱的代码构造。
以下的表达式将导致一个警告,因为程序员很难记住逻辑操作符是左结合还是右结合的,有可能他记错了:
if(a && b || c) . . .
而下面给出警告是因为,在缺少大括号时,if和else语句间的关系可能不是程序员设想的那样:
if(a)
if(b)
m = p;
else
a = 0;
从缩进来看,程序员的意图是else语句对应于第一个if语句,但实际不是这样。
warn_return_type(C,C++,-Wreturn-type,也由-Wall设置)如果非0,警告没有定义返回值或者定义了空返回值而返回了void以外的类型的函数定义。
warn_sequence_point(C,-Wsequence-point,也由-Wall设置)如果非0,警告在一个表达式中一个变量被引用多次,而且其中一次修改其值。在C语言中,允许表达式在序列点间(between sequence points)以任意次序求值(只要操作符的优先级不变),因此在一处修改一个变量而在另一处使用它,会使得其值不确定。序列点(sequence point)由下列操作符在代码中指出:
; , && || ? :
以下是一些违反序列点规则,而导致不确定结果的表达式例子:
s = a[s++];
s = s--;
a[s++] = b[s];
a[s] = b[s += c];
warn_sign_compare(C,C++,ObjC,-Wsign-compare,也由-Wall设置)如果非0,警告有符号及无符号值间的比较。如果为-1,-Wsign-compare及-Wno-sign-compare都没指明使用(这时,由-Wextra来决定)。
warn_switch(C,C++,ObjC,-Wswitch,也由-Wall设置)如果非0,警告一个使用enum的switch,没有default事件,不是所有enum值都有对应事件(case)。
warn_strict_aliasing(-Wstrict-aliasing),依赖被编译的语言,应用最严格的别名规则(the strictest aliasing rules)。在C中,在严格别名下,例如,一个int不能是一个double或指针的别名,但可以是一个unsigned int的别名。即便是严格别名,对于union的数据成员,这都不是问题,只要这些引用是通过union本身,而不是通过指向其地址的指针。下面的代码会有问题。
int *iptr;
union {
int ivalue;
double dvalue;
} migs;
. . .
migs.ivalue = 45;
iptr = &migs.ivalue;
frammis(*iptr);
migs.dvalue = 88.6;
frammis(*iptr);
在这个例子中,很可能严格别名不能识别到iptr指向的值在2个函数调用间被改变了。但直接访问union成员就没有这个问题。
warn_unknown_pragmas(-Wunknown-pragmas,也由-Wall设置)如果非0,警告不能识别的#pragma指示(directive)。
warn_uninitialized(-Winitialized)如果非0,警告一个自动变量未初始化就被使用了。同样警告会破坏自动变量的setjmp调用。这个选项仅能与-O连用,因为要检测这些情况,需要数据流优化信息(the optimizing data flow information)。
因为这个选项要求数据流分析,因此它不可能完全正确。例如,下面的代码中,不可能保证在printf语句前value一定有或一定没有初始化:
int value;
if(a < b)
value = 5;
else if(a > c)
value = 10;
printf("%d/n",value);
因为数据流分析的本质,这个选项不会用于结构体,union,数组,任一声明为volatile的变量,任一被取地址的变量,或任一用作计算从不使用的值的变量。数据流分析能识别setjmp语句,但它没办法知道何处longjmp被调用,因此当没有问题时,依然给出警告。
这个选项当-O被使用时,由-Wall设置。
warn_main(C,C++,-Wmain)如果非0,警告可疑的main函数。
warn_nonvdtor(C++,-Wnon-virtual-dtor,也由-Wall设置)如果非0,警告一个类应该有虚析构函数,而实无。
warn_reorder(C++,-Wreorder,也由-Wall设置)如果非0,警告编译器重新安排类成员,以符合其声明次序。例如,下面的初始化数据将被重新排列:
class Reo {
int i;
int j;
Reo(): j(5), i(10) { }
};
warn_nontemplate_friend(C++,-Wnon-template-friend,也由-Wall设置)如果非0,警告在一个模板中声明了一个非模板化(non-templatized)的友元函数。