目录
1、运行预处理器
2、使用 ASCII 码来生成流图
3、交叉引用输出
cflow 可以在分析之前预处理输入文件,与 cc 在编译之前做的工作一样。这样做可以允许 cflow 准确的处理所有的符号声明,这样就避免了使用--symbol 对特使符号做必要的定义。使用--cpp (--preprocess)选项可以使能预处理功能。对我们的样例 d.c,这种模式输出如下:
$ cflow --cpp -n d.c
1 main() :
2 fprintf()
3 atoi()
4 printdir() (R):
5 getcwd()
6 perror()
7 chdir()
8 opendir()
9 readdir()
10 printf()
11 ignorent() :
12 strcmp()
13 isdir() :
14 stat()
15 perror()
16 putchar()
17 printdir()
(recursive: see 4)
18 closedir()
对比这个图和第 5 章没有使用--cpp 选项的结果。就像你看到的一样,S_ISDIR 不见了。宏被展开了。现在试着运行 cflow --cpp --number -i x d.c 并比较与之对应的没有使用预处理的图。你将会看到不使用--symbol 选项也能生成正确的结果。默认情况下--cpp 运行/usr/bin/cpp。如果你希望运行其他预处理命令,可以在参数中用后接等号后的符号指定它。比如,cflow --cpp='cc -E'将会运行 C 编译器来作为预处理器。
你可以使用--level-indent 选项来配置 cflow 输出的精确格式。最简单的用法是使用这个选项个边每个嵌套级别的缩进长度,他可以指定一个参数值做为每一个嵌套级别的缩进列数。比如下面的指令设置缩进长度为 2,是默认长度的一般:
cflow --level-indent 2 d.c
比如,这样可以保证图在页面的边界之内。
然而--level-indent 能做的不仅仅是这些。每一行都包含下面的图形元素:一个开始标识、一个结束标识和他们之间的几个缩进符。默认情况下,开始标识和结束标识是空的,每个缩进填充包含 4 个空格。
如果--level-indent 选项的参数有 element=string 的形式,它指定一个字符串来代替输出的指定图元素。元素的命名为:
start 开始标识
0 缩进填充 0
1 缩进填充 1
end0 结束标识 0
end1 结束标识 1
为审美会有两种类型的缩进填充和结束标识呢?记住这个流图代表了一个调用树,所以它包含终端节点(叶子),比如这是一个调用结束函数。还有非终端结点(这个调用后接另一个嵌套级的调用)。end0 表示非终端结点,end1 表示终端节点。
对于缩进填充,缩进填充 1 用于代表图的边界,缩进填充 0 用于保持输出图的格式对齐。
为了论证这个,我们考虑下面的例子程序:
/* foo.c */
int
main()
{
f();
g();
f();
}
int
f()
{
i = h();
}
我们用下面的字符串表示行元素:
start ‘::’
0 ‘ ’ (two spaces)
1 ‘| ’ (a vertical bar and a space)
end0 ‘+-’
end1 ‘\-’
这个对应的命令行是:
cflow --level begin=:: --level '0= ' --level '1=| ' --level end0='+-' --level end1='\\-' foo.c
注意转义处理 end1 的反斜杠:一般来说,在--level-option 选项中的字符串可以包含常用的转义字符序列,所以反斜杠自己必须被转义。另一个捷径是,云溪在字符串中出现 CxN,C 表示一个单字符、N 表示一个十进制数字。这个符号的意思是“重复字符 C N 次”。然而当字符’x’用在字符串的开头时就失去了它的专用意义。这个命令产生下面的结果:
::+-main() :
:: +-f() :
:: | \-h()
:: \-g()
因此,我们可以用 ASCII 码表来表示调用树。GNU cflow 提供了一个特殊的选项--tree (-T),它是--level '0= ' --level '1=| ' --level end0='+-' --level end1='\\-'参数的快捷方式。下面这个例子是由这个选项生成的流图。源文件 wc.c 是 UNIX的 wc 命令的简单实现,附加在附录中。
+-main()
+-errf()
| \-error_print()
|
| +-vfprintf()
| +-perror()
| +-fprintf()
| \-exit()
+-counter()
| +-fopen()
| +-perrf()
| | \-error_print()
| | [see 3]
| +-getword()
| | +-feof()
| | \-isword()
| | \-isalpha()
| +-fclose()
| \-report()
|
| \-printf()
\-report()
[see 17]
cflow 也可以生成交叉引用列表。这个模式使用-xref(-x)使能。交叉引用输出在单独的一行列出了每一个符号。每一行显示了标示符和和他出现的源位置。如果这个位置是该符号定义的地方,那么这一行用星号额外标识,并在后面附加它的定义。例如,下面是 d.c 程序的一个交叉引用输出段:
printdir * d.c:42 void printdir (int level,char *name)
printdir d.c:74
printdir d.c:102
它显示 printdir 函数在第 42 行定义,被引用两次分别在第 74 和 102 行。出现在交叉引用列表中的符号受到--include 选项的控制。除此之外在“控制符号类型”一节中讲述的字符类,可用一个额外的符号类 t 控制背 typedef 关键自定义的类型名。
带你走进Cflow (一)-CSDN博客
带你走进Cflow (二)·输出格式和递归调用-CSDN博客
带你走进Cflow (三)·控制符号类型分析-CSDN博客