带你走进Cflow (三)·控制符号类型分析

目录

​编辑

1、控制符号类型

[1.1 语法类](#1.1 语法类)

[1.2 符号别名](#1.2 符号别名)

[1.3 GCC 初始化](#1.3 GCC 初始化)


1、控制符号类型

有人也许注意到了输出中奇怪的现象:函数_exit 丢失了,虽然它在源文件中被printdir 调用了两次。这是因为默认情况下 cflow 忽略所有的一下划线开头的符号。为了将这样的符号包含进来,我们使用-i _ (or --include _)命令行选项。继续我们的例子:

$ cflow --number -i _ d.c
 1 main() <int main (int argc,char **argv) at d.c:85>:
 2 fprintf()
 3 atoi()
 4 printdir() <void printdir (int level,char *name) at d.c:42> (R):
 5 getcwd()
 6 perror()
 7 _exit()
 8 chdir()
 9 opendir()
 10 readdir()
 11 printf()
 12 ignorent() <int ignorent (char *name) at d.c:28>:
 13 strcmp()
 14 isdir() <int isdir (char *name) at d.c:12>:
 15 stat()
 16 perror()
 17 S_ISDIR()
 18 putchar()
 19 printdir()
 <void printdir (int level,char *name) at d.c:42>
 (recursive: see 4)
 20 closedir()

通常情况下,参数--include 制定了一个符号类列表。默认的选项行为是输出中包含被请求的类。如果参数是以减号或插入符号开始的,则处理方式正好相反,是在输出结果中排除这种符号类。

符号类'_'包含了所有已下划线开头的符号。另一个有用的符号类是's',它表示静态函数或数据。默认情况下,静态函数是被包含在输出中的。为了省略他们,可以使用所给的-i ^s (or -i -s)选项。我们的样例程序 d.c 中定义了静态函数 isdir,运行 cflow -i ^s,可以在结果图中完全忽略这个函数和它的调用者。

$ cflow --number -i ^s d.c
 1 main() <int main (int argc,char **argv) at d.c:85>:
 2 fprintf()
 3 atoi()
 4 printdir() <void printdir (int level,char *name) at d.c:42> (R):
 5 getcwd()
 6 perror()
 7 chdir()
 8 opendir()
 9 readdir()
 10 printf()
 11 ignorent() <int ignorent (char *name) at d.c:28>:
 12 strcmp()
 13 putchar()
 14 printdir()
 <void printdir (int level,char *name) at d.c:42>
 (recursive: see 4)
 15 closedir()

实际上,非包含符号('^' 或 '-')可以在-i 的参数中的任何地方使用,不仅限于开始。因此,选项-i _^s 表示包含除了静态函数以外的以下划线开头的符号。-i 选项可以累积使用,所以这个例子可以写为-i _ -i ^s。

一个很重要的一点是,默认情况下 cflo 图中只包含函数。然而你可以使用符号类"x"来将变量输出。这个类包含所有的数据符号,包括全局变量和静态变量。如下例:

$ cflow --number -i x d.c
 1 main() <int main (int argc,char **argv) at d.c:85>:
 2 fprintf()
 3 stderr
 4 max_level <int max_level at d.c:37>
 5 atoi()
 6 printdir() <void printdir (int level,char *name) at d.c:42> (R):
 7 DIR
 8 dir
 9 getcwd()
 10 perror()
 11 chdir()
 12 opendir()
 13 readdir()
 14 printf()
 15 ignorent() <int ignorent (char *name) at d.c:28>:
 16 ignored_names <char *ignored_names[] at d.c:24>
 17 strcmp()
 18 isdir() <int isdir (char *name) at d.c:12>:
 19 stat()
 20 perror()
 21 S_ISDIR()
 22 NULL
 23 max_level <int max_level at d.c:37>
 24 putchar()
 25 printdir()
 <void printdir (int level,char *name) at d.c:42>
 (recursive: see 6)
 26 closedir()

现在,第 3、4、16、23 行显示了数据标号,同时显示了可用的定义。然而请注意第 7、8 行。为什么类型名 DIR 和自动变量 dir 也被作为数据列出?为了回答这个问题,我们首先描述一线 cflow 对符号的概念定义。程序维持着符号表,使用 C 语言预处理关键字初始化。当解析输入文件时,cflow 更新了这些表。特别的,当遇到一个 typedef 时,它就将这个定义符号注册为数据类型。现在,DIR 在 d.c 中没有声明,所以 cflow 无法知道这是一个数据类型。所以他认为这是一个变量,这样一来输入:DIR *dir;就被解析为一个表达式,意思是"DIR乘以 dir"。

当然这是错误的。有两种方式可以帮助 cflow 排除这种混淆。要么详细的声明 DIR为一个数据类型,要么让 cflow 运行预处理器,这样一来 cflow 就能看到头文件的内容,并自己做出决定。运行预处理器在下一章中。这一章我们主要集中精力在第一种方法。

命令行选项--symbol (-s)声明这个符号的语法类。这个参数包含了被冒号隔开的两个字符串:--symbol sym:class。

第一个字符串,sym 是 C 语言的符号表的识别码。第二个字符串,class,指定了与这个符号结合的类。特别的如果 class 是'type',那么符号 sym 就被记录为 C 语言类型定义。因此,修改上面的输出,运行:

$ cflow --number -i x --symbol DIR:type d.c

另一个重要的符号类型是参数包装,这是宏的一种,经常用于兼容 ANSI 之前的编 译 器 来 保 护 函 数 原 型 中 的 参 数 声 明 的 源 。 比 如 下 面 的 声 明 , 从/usr/include/resolv.h 中获得,其中__P 是参数包装:

void res_npquery __P((const res_state, const u_char *, int, FILE *));

为了能让 cflow 处理这样的声明,声明__P 为一个包装,例如:

cflow --symbol __P:wrapper *.c

在所有的必须使用--symbol 选项的例子中,都是 cflow 不能识别给定的符号的意义,这要么是因为 cflow 不能看到类型的定义,就像'DIR'的例子;要么是因为宏定义没有展开。这两种情况都可以用下一章藐视的预处理模式来解决。虽然有了预处理模式,但--symbol 选项还是有用的,我们会在下面的一节中看到:

1.1 语法类

总的来说,符号定义的语法类在 C 语言代码中是可以合法存在的。有如下的类:

关键字;关键字,比如'if'、'when'等

修改器;类型修改器,比如这个符号在数据类型后出现,可以修改数据的意义,比如指针'*'。

修饰符;声明修饰符。能在 C 数据类型的前面和后面声明。你也许会经常声明gcc 关键字'extension'作为修饰符:--symbol extension:qualifier

识别码;C 语言识别码。

类型;C 语言数据类型,比如'char','int'。

包装器;他有两个用途,第一个是当没有运行预处理的时候声明一个参数包装器。这种用法在前面已经声明了。第二种,他表示任何能出现在声明符前或作为结尾的分号之前和后面可以跟一个括号表达式列表中的任何符号。

我们建议这样对 gcc 使用这个类:'attribute'。

1.2 符号别名

另一个--symbol 选项的用法是定义符号别名。别名是一个与被它引用的符号完全一样的标识。别名可以这样声明:--symbol newsym:=oldsym这样一来,符号 newsym 就被声明为和 oldsym 完全一样的类型了。

符号别名也能在其他例子中作为定义符号类使用。对一些特殊的关键字是非常有用的,例如'__restrict': --symbol __restrict:=restrict。

1.3 GCC 初始化

下面的引用集是使用 gcc 时 cflow 的初始化选项。我们建议将他们放到 cflow.rc文件中:

--symbol __inline:=inline
 --symbol __inline__:=inline
 --symbol __const__:=const
 --symbol __const:=const
 --symbol __restrict:=restrict
 --symbol __extension__:qualifier
 --symbol __attribute__:wrapper
 --symbol __asm__:wrapper
 --symbol __nonnull:wrapper
 --symbol __wur:wrapper

带你走进Cflow (一)-CSDN博客

带你走进Cflow (二)·输出格式和递归调用-CSDN博客

相关推荐
问道飞鱼1 小时前
【知识科普】认识正则表达式
数据库·mysql·正则表达式
HaiFan.1 小时前
SpringBoot 事务
java·数据库·spring boot·sql·mysql
水根LP491 小时前
linux系统上SQLPLUS的重“大”发现
数据库·oracle
2401_882727571 小时前
低代码配置式组态软件-BY组态
前端·后端·物联网·低代码·前端框架
NoneCoder1 小时前
CSS系列(36)-- Containment详解
前端·css
anyup_前端梦工厂2 小时前
初始 ShellJS:一个 Node.js 命令行工具集合
前端·javascript·node.js
5hand2 小时前
Element-ui的使用教程 基于HBuilder X
前端·javascript·vue.js·elementui
途途途途2 小时前
精选9个自动化任务的Python脚本精选
数据库·python·自动化
GDAL2 小时前
vue3入门教程:ref能否完全替代reactive?
前端·javascript·vue.js
六卿2 小时前
react防止页面崩溃
前端·react.js·前端框架