圈复杂度 (Cyclomatic Complexity)

圈复杂度,又称条件复杂度,是评估代码复杂性的指标,与测试用例数和程序维护难度有关。计算公式为V(G) = E - N + 2,其中E表示边数,N表示点数。高圈复杂度增加错误风险,建议通过提炼函数、使用break/return、简化条件等方式降低。常用的圈复杂度检查工具有OCLint、GMetrics、PyMetrics、JSComplexity、sourcemonitor和sonarqube。
概念

圈复杂度也称条件复杂度,是一种衡量代码复杂度的标准。它可以用来衡量一个模块判定结构的复杂程度,数量上表现为独立现行路径条数,也可以理解为覆盖所有情况最少使用的测试用例数。圈复杂度大说明程序代码的判断逻辑复杂,可能难以维护。

计算方法

V(G) = E - N + 2;

其中,E表示控制流图中边的数量,N表示控制流图中点的数量。

下面举例说明,如if-else,while,until和正常的顺序:

squence

A;
B;
A
B

if - else

A;
if(some_condition)
	then 
		B;
	else
		C;
end if
D;
A
B
C
D

while

A;
while(some_condition) 
	do 
		B;
	end
C;
A
B
C

until

A;
repeat
	B;
until(some_condition);
C;
A
B
C

举个例子:

void func(){
	a;
	while(A)
	{
		if(B || C)
			b;
		c; 
	}
	if(D && E) {
		d;
		for(F)
		{
			if(G || H || I)
				e;
		}
	}
	if(J && K)
		h;
	i; 
};

那么上面的复杂度为:
A(while)+B(if)+C(if)+D(if)+E(if)+F(for)+G(if)+H(if)+I(if)+J(if)+K(if)+1 = 12

注意||和&&也会被算作一个判定节点

圈复杂度的意义

一般来说,圈复杂度大存在很大的出错风险。另外在测试的时候,一个很好的用例设计经验:创建与圈复杂度值相等的测试用例,以此提高用例对代码的分支覆盖率。

降低圈复杂度的方法
  1. 提炼函数,将复杂的分支隔离,换成call func()调用函数。

  2. 使用break,return代替控制标记。

  3. 使用数据结构,算法。

  4. 多态取代条件式,设计模式替换。

  5. 简化条件判断。
    举个例子:

    string fun(string key) {
    	if(key == "A") {
    		return "a";
    	} else if(key == "B") {
    		return "b"
    	} else if(key == "C") {
    		return "c"
    	};
    	return string;
    }
    

    改成

    string fun(string key) {
    	map<string,string> maps;
    	maps["A"]="a";
    	maps["B"]="b";
    	maps["C"]="c";
    	return maps[key];
    }
    
常用工具
  1. OCLint(C语言),GMetrics(Java),PyMetrics(Python),JSComplexity(js)
  2. Lizard,sourcemonitor(支持多种语言)
  3. sonarqube(平台)
**Cyclomatic Complexity复杂度)** 是一种软件度量,用于衡量程序中**控制流的复杂程度**。它由 Thomas McCabe 在 1976 年提出,常用于评估一个函数或模块的**可测试性、可维护性和潜在风险**。 --- ### ✅ 复杂度的定义: > 复杂度Cyclomatic Complexity) = **程序中独立路径的数量**。 它帮助我们了解: - 一个函数有多少条不同的执行路径 - 需要多少个测试用例才能完全覆盖 - 函数的逻辑是否过于复杂,是否需要重构 --- ### 🔢 复杂度的计算方法: #### 方法一(基于控制流图): $$ V(G) = E - N + 2P $$ - E:图中的边数(edges) - N:图中的节点数(nodes) - P:图中连通组件的数量(通常是 1) #### 方法二(基于代码结构): 从代码出发,每遇到一个控制结构就加 1: | 控制结构 | 增加的复杂度 | |------------------|----------------| | if / else if / ? | 1 | | for / while | 1 | | do-while | 1 | | switch-case | 每个 case 标签 1 | | && / \|\| | 1(每个短路操作)| --- ### ✅ 示例: ```c int checkValue(int a, int b) { if (a > 0 && b < 10) { // +2(两个条件) return 1; } else { return 0; } } ``` 复杂度 = 1(入口)+ 2(两个条件)= **3** 这意味着该函数有 3 条独立路径。 --- ### ✅ 复杂度的意义: | 复杂度范围 | 含义 | |---------------|----------------------------------| | 1 - 10 | 简单,结构良好,易于测试和维护 | | 11 - 20 | 中等复杂,可能需要重构 | | 21 - 50 | 高复杂度,难于维护,风险较高 | | > 50 | 极其复杂,应彻底重构 | --- ### ✅ MISRA C 和复杂度: MISRA C 建议函数的复杂度不应超过 **15**(建议值),以确保代码的可读性和可维护性。 --- ### ✅ 如何降低复杂度: - 提取部分逻辑为子函数 - 减少嵌套 if/else - 使用策略模式或状态机替代复杂的 switch-case - 使用早返回(early return)减少嵌套层级 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值