3

我想知道面对 C/C++ 代码中的 ifdefs,如何计算标准代码复杂度指标(例如 LOC、McCabe 圈复杂度、Halstead 指标等)。

如果忽略 ifdefs,可能会出现语法错误(甚至类型错误),例如:

  1. #ifdef FOO
  2. for(i = 0; i < x; i++) {
  3. #别的
  4. for(i = 0; i < y; i++) {
  5. #万一
  6. 打印(...)
  7. }

如果忽略第 1、3 和 5 行,则生成的代码将在另一个循环中包含一个循环,并且缺少“}”(因此出现语法错误)。

如果考虑到#ifdefs,那么每个变体都需要一个结果度量(在这种情况下,一个定义 FOO 的情况,另一个用于未定义 FOO 的情况)。然而,这种方法在实践中很容易爆炸。

查看http://manpages.ubuntu.com/manpages/natty/man1/pmccabe.1.html,手册页报告说:

解析 pmccabe 会忽略所有 cpp 预处理器指令 - 计算代码外观的复杂性,而不是预处理器破坏代码后的复杂性。这一点尤其重要,因为像 getchar(3) 这样简单的东西会扩展成增加复杂性的宏。

但是,正如我所说,这种方法可能会导致出现代码错误,进而可能会阻碍计算过程或导致错误的值。

工具如何克服这个问题(如果它们完全克服的话)?

问候。

4

3 回答 3

2

如果您将该构造重写为

#ifdef FOO
# define LOOPEND x
#else
# define LOOPEND y
#endif

或类似的东西,应该没有问题。

如果您不想这样做,并且您的复杂性检查器不理解预处理器,您可以在预处理器的输出上运行它,无论是FOO定义的还是FOO未定义的,然后取它产生的最大值以获得实际复杂度的下限。gcc -E使用 GCC,预处理器使用;调用。其他编译器将有类似的选项。

(当然,如果你有很多这种 CPP 魔法,你就会有指数级的可能性。但这就是你为使用预处理器付出的代价;这同样适用于测试。)

于 2013-02-25T16:16:14.270 回答
1

许多度量计算器不使用完整或一致的解析器,而是使用(可能)翻译的前几个阶段,然后查找关键字。

因为他们没有像你描述的那样查看完整的语法问题,所以不要阻止他们。然而,它们确实在其他方面变得脆弱,事实上我已经看到 cyclo-2.0 在可靠的生产代码上完全失败。

于 2013-02-25T16:18:15.400 回答
0

我原以为在一个更大的项目(而不是 20 行“实验”)中#ifdef,影响代码实际复杂性的数量和类似的东西会相当少——你希望大多数这类表达式都在对总分影响不大的独立单元。

但是,是的,如果您有一小部分代码,其中包含大量#ifdef且复杂的#ifdef部分,您可能会遇到很大的错误。手动检查代码将能够告诉您这一点。

于 2013-02-25T16:20:22.660 回答