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

2023-11-11 06:15

本文主要是介绍带你走进Cflow (三)·控制符号类型分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

​编辑

1、控制符号类型

1.1  语法类

1.2  符号别名

1.3  GCC 初始化


1、控制符号类型

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

$ cflow --number -i _ d.c1 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.c1 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.c1 main() <int main (int argc,char **argv) at d.c:85>:2 fprintf()3 stderr4 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 DIR8 dir9 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 NULL23 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博客

这篇关于带你走进Cflow (三)·控制符号类型分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/388092

相关文章

Redis连接失败:客户端IP不在白名单中的问题分析与解决方案

《Redis连接失败:客户端IP不在白名单中的问题分析与解决方案》在现代分布式系统中,Redis作为一种高性能的内存数据库,被广泛应用于缓存、消息队列、会话存储等场景,然而,在实际使用过程中,我们可能... 目录一、问题背景二、错误分析1. 错误信息解读2. 根本原因三、解决方案1. 将客户端IP添加到Re

Mysql 中的多表连接和连接类型详解

《Mysql中的多表连接和连接类型详解》这篇文章详细介绍了MySQL中的多表连接及其各种类型,包括内连接、左连接、右连接、全外连接、自连接和交叉连接,通过这些连接方式,可以将分散在不同表中的相关数据... 目录什么是多表连接?1. 内连接(INNER JOIN)2. 左连接(LEFT JOIN 或 LEFT

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

Redis的Hash类型及相关命令小结

《Redis的Hash类型及相关命令小结》edisHash是一种数据结构,用于存储字段和值的映射关系,本文就来介绍一下Redis的Hash类型及相关命令小结,具有一定的参考价值,感兴趣的可以了解一下... 目录HSETHGETHEXISTSHDELHKEYSHVALSHGETALLHMGETHLENHSET

锐捷和腾达哪个好? 两个品牌路由器对比分析

《锐捷和腾达哪个好?两个品牌路由器对比分析》在选择路由器时,Tenda和锐捷都是备受关注的品牌,各自有独特的产品特点和市场定位,选择哪个品牌的路由器更合适,实际上取决于你的具体需求和使用场景,我们从... 在选购路由器时,锐捷和腾达都是市场上备受关注的品牌,但它们的定位和特点却有所不同。锐捷更偏向企业级和专

Python中异常类型ValueError使用方法与场景

《Python中异常类型ValueError使用方法与场景》:本文主要介绍Python中的ValueError异常类型,它在处理不合适的值时抛出,并提供如何有效使用ValueError的建议,文中... 目录前言什么是 ValueError?什么时候会用到 ValueError?场景 1: 转换数据类型场景

Spring中Bean有关NullPointerException异常的原因分析

《Spring中Bean有关NullPointerException异常的原因分析》在Spring中使用@Autowired注解注入的bean不能在静态上下文中访问,否则会导致NullPointerE... 目录Spring中Bean有关NullPointerException异常的原因问题描述解决方案总结

python中的与时间相关的模块应用场景分析

《python中的与时间相关的模块应用场景分析》本文介绍了Python中与时间相关的几个重要模块:`time`、`datetime`、`calendar`、`timeit`、`pytz`和`dateu... 目录1. time 模块2. datetime 模块3. calendar 模块4. timeit

C# dynamic类型使用详解

《C#dynamic类型使用详解》C#中的dynamic类型允许在运行时确定对象的类型和成员,跳过编译时类型检查,适用于处理未知类型的对象或与动态语言互操作,dynamic支持动态成员解析、添加和删... 目录简介dynamic 的定义dynamic 的使用动态类型赋值访问成员动态方法调用dynamic 的

python-nmap实现python利用nmap进行扫描分析

《python-nmap实现python利用nmap进行扫描分析》Nmap是一个非常用的网络/端口扫描工具,如果想将nmap集成进你的工具里,可以使用python-nmap这个python库,它提供了... 目录前言python-nmap的基本使用PortScanner扫描PortScannerAsync异