有些C程序,在编写的时候,为了应对多种情况(比如说支持多平台,选择某些特性等) ,就在源码当中使用了很多的宏来控制。当某些宏打开的时候某些代码才生效。如果代码当中,这些宏比较少,那还没什么,但是当代码当中有极多的宏的时候,阅读起来就很头疼了。如果有朋友看过QEMU的源码,会深有同感的。它的源码当中定义了很多的宏,有些函数针对某个平台的代码并不长,但是为了支持多平台,它就要定义不同的宏来控制在不同平台上对应的代码起作用。这对编译器是好事,但是如果我们只想看某个平台相关的代码就很麻烦了,以前很是苦恼,找不到很好的解决这个问题的办法。
最近在用GCC的时候,发现其中的一些命令选项可以帮助我解决这个问题。基本的思想是让GCC在编译的时候不删除临时文件,而这些临时文件就包含了经过预处理之后的代码,这时候就不会有宏的干扰了,但是也失去了宏原本的意义(比如说某些常量,使用宏的话,可以见名知义)。举个例子来说,我们有这么一个C程序a.c
#include
<
stdio.h
>
int
main(
int
argc,
char
*
argv[])
{
int arch;
#ifdef _I386_
arch = 1;
#elif _MIPS_
arch = 2;
#elif _ARM_
arch = 3;
#else
#error "Give me a architecture select!"
#endif
return 0;
}
如果我们输入命令:
$ gcc -D_MIPS_ -save-temps a.c
这时候-save-temps选项告诉gcc在编译的过程中不要删除临时文件,那么在当前文件夹下就会产生:
1)a.i : 是经过C预处理程序(CPP)处理之后的文件,这个就是我们以后工作的基础
2)a.s: 是经过汇编器处理之后产生的汇编代码
3)a.o: 是编译器产生的目标代码
4)a.out: 是编译器默认产生的可执行程序。
我们主要在a.i的上面进行操作,如果我们想看MIPS平台相关的代码,我们就使用上面的命令产生a.i,我们来看a.i的内容, 由于内容比较多,我这里只给出前10行和主体代码部分:
#
1
"
a.c
"
#
1
"
<built-in>
"
#
1
"
<command line>
"
#
1
"
a.c
"
#
1
"
/usr/include/stdio.h
"
1
3
4
#
28
"
/usr/include/stdio.h
"
3
4
#
1
"
/usr/include/features.h
"
1
3
4
#
329
"
/usr/include/features.h
"
3
4
#
1
"
/usr/include/sys/cdefs.h
"
1
3
4
#
313
"
/usr/include/sys/cdefs.h
"
3
4

...............................

#
850
"
/usr/include/stdio.h
"
3
4

#
2
"
a.c
"
2
int
main(
int
argc,
char
*
argv[])
{
int arch;



arch = 2;





return 0;
}
可以看出来,文件的内容基本是这么几个组成部分:
1) #include 语句会被扩展开来,并把对应内容包含进来,同时加入 “# _line_number "_include_file_name_
" xx xx" 这些信息
2) 源文件当中的信息会有所保留,宏被展开,注释被换成空行。条件宏起作用,其所在的行被换成空行。
我们要做的就是把这些以#号开头的行和所有空行删除,并重新对a.i文件进行排版,那么它成了一个我们希望要的经过宏展开之后的C文件。
如果文件数目不多,我们可以使用这个命令 $ gcc -D_MIPS_ -E -P a.c >t.c 来实现上述功能,这个命令就是告诉GCC只进行预编译,并且在预编译的时候不产生“#include"语句和一些不必要的空行。但是当文件很多,特别是我们使用Makefile文件来控制我们整个工程的编译的时候,这个方法就不合适了,就需要使用我前面提到的第
一种方法,具体做法如下:
1) 在Makfile文件当中的CFLAGS定义你想要的宏,并加上-save-temps选项,对于我们这个例子,我们可以定义: CFLAGS +=-D_MIPS -save-temps,然后执行make命令。执行成功之后,会生成一堆的 .i文件,下一步我么要操作.i文件。
2)执行这么一个脚本,它的作用就是把当前目录下的所有以.i作为后缀名的文件当中的 “#include" 行和空行删除。
#
!/bin/bash
#filename: clearify.sh
for
f in
./*.
i
do
name
=
`basename
$f
`
sed
"
/^[[:space:]]*$/d
"
$name
>
t1
sed
"
/^# .*$/d
"
t1
>
$name
done
如果你觉得文件排版不好看,还可以使用 $ indent -kr file_name 来进行美化排版。
希望对大家阅读含有很多宏的源码时有所帮助。
最近在用GCC的时候,发现其中的一些命令选项可以帮助我解决这个问题。基本的思想是让GCC在编译的时候不删除临时文件,而这些临时文件就包含了经过预处理之后的代码,这时候就不会有宏的干扰了,但是也失去了宏原本的意义(比如说某些常量,使用宏的话,可以见名知义)。举个例子来说,我们有这么一个C程序a.c















这时候-save-temps选项告诉gcc在编译的过程中不要删除临时文件,那么在当前文件夹下就会产生:
1)a.i : 是经过C预处理程序(CPP)处理之后的文件,这个就是我们以后工作的基础
2)a.s: 是经过汇编器处理之后产生的汇编代码
3)a.o: 是编译器产生的目标代码
4)a.out: 是编译器默认产生的可执行程序。
我们主要在a.i的上面进行操作,如果我们想看MIPS平台相关的代码,我们就使用上面的命令产生a.i,我们来看a.i的内容, 由于内容比较多,我这里只给出前10行和主体代码部分:































1) #include 语句会被扩展开来,并把对应内容包含进来,同时加入 “# _line_number "_include_file_name_
" xx xx" 这些信息
2) 源文件当中的信息会有所保留,宏被展开,注释被换成空行。条件宏起作用,其所在的行被换成空行。
我们要做的就是把这些以#号开头的行和所有空行删除,并重新对a.i文件进行排版,那么它成了一个我们希望要的经过宏展开之后的C文件。
如果文件数目不多,我们可以使用这个命令 $ gcc -D_MIPS_ -E -P a.c >t.c 来实现上述功能,这个命令就是告诉GCC只进行预编译,并且在预编译的时候不产生“#include"语句和一些不必要的空行。但是当文件很多,特别是我们使用Makefile文件来控制我们整个工程的编译的时候,这个方法就不合适了,就需要使用我前面提到的第
一种方法,具体做法如下:
1) 在Makfile文件当中的CFLAGS定义你想要的宏,并加上-save-temps选项,对于我们这个例子,我们可以定义: CFLAGS +=-D_MIPS -save-temps,然后执行make命令。执行成功之后,会生成一堆的 .i文件,下一步我么要操作.i文件。
2)执行这么一个脚本,它的作用就是把当前目录下的所有以.i作为后缀名的文件当中的 “#include" 行和空行删除。









希望对大家阅读含有很多宏的源码时有所帮助。