DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report

What Exactly Is McCabe Cyclomatic Complexity?

Learning how to properly use the McCabe method and what kinds of code it applies to can give an accurate measure of the complexity of your code.

Reginald Stadlbauer user avatar by
Reginald Stadlbauer
·
Apr. 10, 18 · Tutorial
Like (2)
Save
Tweet
Share
25.80K Views

Join the DZone community and get the full member experience.

Join For Free

The cyclomatic complexity is a measurement of the code complexity proposed by McCabe which is often considered as a magic number which allows us to measure the complexity of a program. It is common to say that a function with a cyclomatic complexity higher than 10 is difficult to maintain due to an over-reliance on branches, switch/cases or loops. These functions are often a good candidate for a redesign.

How good is this metric? Would the number of lines of code give us the same result? If that were true, it would make more sense to the simpler metric since everybody intuitively understands it.

To answer this question, I will expose how to compute it and then analyze some properties.

How to Compute the Cyclomatic Complexity

The formula of the cyclomatic complexity of a function is based on a graph representation of its code. Once this is produced, it is simply:

M = E – N + 2

E is the number of edges of the graph, N is the number of nodes and M is McCabe’s complexity.

To compute a graph representation of code, we can simply disassemble its assembly code and create a graph following the rules:

  1. Create one node per instruction.
  2. Connect a node to each node which is potentially the next instruction.
  3. One exception: for a recursive call or function call, it is necessary to consider it as a normal sequential instruction. If we don’t do that, we would analyze the complexity of a function and all its called subroutines.

Let have a try with a small sample:

#include <stdio.h>

int main( int argc, char *argv[] )
{
    if ( argc > 1 )
    {
        fprintf( stderr,
                "This program does not accept arguments!\n"
               );
        return 1;
    }
    else
    {
        fprintf( stdout, "Hello world!\n" );
        return 0;
    }
}

The assembly output is:

%struct._IO_FILE = type { i32, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, %struct._IO_marker*, %struct._IO_FILE*, i32, i32, i64, i16, i8, [1 x i8], i8*, i64, i8*, i8*, i8*, i8*, i64, i32, [20 x i8] }
%struct._IO_marker = type { %struct._IO_marker*, %struct._IO_FILE*, i32 }

@stderr = external global %struct._IO_FILE*, align 8
@.str = private unnamed_addr constant [41 x i8] c"This program does not accept arguments!\0A\00", align 1
@stdout = external global %struct._IO_FILE*, align 8
@.str.1 = private unnamed_addr constant [14 x i8] c"Hello world!\0A\00", align 1

; Function Attrs: nounwind uwtable
define i32 @main(i32 %argc, i8** %argv) #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  %3 = alloca i8**, align 8
  store i32 0, i32* %1
  store i32 %argc, i32* %2, align 4
  store i8** %argv, i8*** %3, align 8
  %4 = load i32, i32* %2, align 4
  %5 = icmp sgt i32 %4, 1
  br i1 %5, label %6, label %9

; <label>:6                                       ; preds = %0
  %7 = load %struct._IO_FILE*, %struct._IO_FILE** @stderr, align 8
  %8 = call i32 (%struct._IO_FILE*, i8*, ...) @fprintf(%struct._IO_FILE* %7, i8* getelementptr inbounds ([41 x i8], [41 x i8]* @.str, i32 0, i32 0))
  store i32 1, i32* %1
  br label %12

; <label>:9                                       ; preds = %0
  %10 = load %struct._IO_FILE*, %struct._IO_FILE** @stdout, align 8
  %11 = call i32 (%struct._IO_FILE*, i8*, ...) @fprintf(%struct._IO_FILE* %10, i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.1, i32 0, i32 0))
  store i32 0, i32* %1
  br label %12

; <label>:12                                      ; preds = %9, %6
  %13 = load i32, i32* %1
  ret i32 %13
}

We can then create a graphical representation of the assembly instruction flow:

Cyclomatic Complexity Seen on the Assembly Level

We have 19 nodes and 19 edges, McCabe’s complexity is then:

M = 19 – 19 + 2 = 2.

Properties

The McCabe’s formula 'E – N + 2'  in the first approach is strange because adding or subtracting two measures (here number of nodes and number of edges) expects that they have the same units. That’s why a distance is never added to a duration in physic but in most of the cases multiplied or divided.

But, in the case of McCabe, this formula is simply the number of independent paths that exist between the input of the function and its output. For our example, the cyclomatic complexity is 2: one for the path for the ‘if’ branch plus one for the path of the ‘else’ case.

This has also a number of implications:

  1. McCabe is independent of the number of source lines of a function; it does not matter how many statements are present in the code, but how many branches are into it.
  2. The metric is completely independent of the code style. Reformatting the code does not affect it. This is a big difference to the common line of code metric.
  3. The metric has a linear grow with the function's complexity. For example, adding an ‘if ( … ) … else …‘ statement somewhere in a function increments the metric by one. It does not matter if the selection statement is nested or at the beginning of the function and it does not grow exponentially.
  4. McCabe is easy to interpret for a single function. But its computation for a source file, a class or an application is difficult to interpret. The reasons for this is that the function call is not represented in the flow diagram. The McCabe metric is computed independently for each function and it is as if there are interactions between the functions present in the source code.
  5. McCabe is not adapted to measure the complexity of the software architecture. Or, to say it differently, it does not work on a complex C++/C# design pattern which splits the complexity over several classes.
  6. And of course, like for all metrics, McCabe’s measure is only of code complexity and not the complexity of the data structures.

Analysis of Some Results

Computing the cyclomatic complexity on some samples allows us to better identify how source code affects the metric.
Here are some general rules:

  1. Adding an ‘else’ block to an ‘if’ statement does not affect its metric (see sample I1 and I2).
  2. The complexity of a boolean expression has no impact on the code metric (see sample I2 and I3). McCabe can be compared to the decision coverage for which it is important to track if a complete boolean expression was true and false.
  3. If a function is composed of two blocks, the cyclomatic complexity of the function is the sum of the complexity of each of it. If we take I2 and add an ‘if’ statement (see sample I4), the complexity is incremented because a single ‘if’ statement has a complexity of 1.
  4. It does not matter if some code is composed of two sequential or two nested parts. I4 and I5 illustrate this: cascading ‘if’ statements have the same effect on the metric as sequential if statements.
  5. All loops are equivalent to an ‘if’ statement (see L1, L2, L3).
  6. Adding a ‘case’ to a ‘switch’ statement increments the cyclomatic complexity. (see S1 and S2)
# Code McCabe
I1
void foo( int a ) {
  if ( a )
     return ;
  else
     return ;
}
2
I2
void foo( int a ) {
  if ( a )
     return ;
}
2
I3
void foo( int a, int b ) {
  if ( a && b )
     return ;
}
2
I4
void foo( int a, int b ) {
  if ( a )
     return ;
  if ( b )
     return ;
}
3
I5
void foo( int a, int b ) {
  if ( a )
     if ( b )
        return ;
}
3
I1
void foo( int a ) {
  while ( a )
     a-- ;
}
2
I2
void foo( int a ) {
  do
     a-- ;
  while ( a );
}
2
I3
void foo( int a ) {
  for ( int x = 0; x < a; x++ )
     foo() ;
}
2
S1
void foo( int a ) {
  switch ( a )
  {
    case 1:
     return ;
  }
}
2
S2
void foo( int a ) {
  switch ( a )
  {
    case 1:
    case 2:
     return ;
  }
}
3

Conclusion

McCabe’s cyclomatic complexity is a simple and intuitive metric that allows us to achieve reliable results at the function level. It does not cover all kinds of software complexity but it allows us to clearly identify functions with a lot of loops, selection statements, and switch/cases. That’s why we have also included it in Squish Coco v4.2 and above.

Cyclomatic Complexity Metric (unit)

Published at DZone with permission of Reginald Stadlbauer. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • 4 Best dApp Frameworks for First-Time Ethereum Developers
  • What “The Rings of Power” Taught Me About a Career in Tech
  • Benefits and Challenges of Multi-Cloud Integration
  • Use AWS Controllers for Kubernetes To Deploy a Serverless Data Processing Solution With SQS, Lambda, and DynamoDB

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: