Release RMC-06.02
(2024-04-29)
|
ContentsMetrics OverviewFunction-Based Metrics File-Based Metrics Class-Based Metrics Project-Wide Metrics General Metrics OverviewThe Calculation of MetricsQAC calculates metrics in three groups. QAC++ calculates metrics in three groups. All metrics, except LIN and TPP, ignore code that is disabled on
evaluation of pre-processor directives. For example: #define A 10 #if 0 #include <stdio.h> /* Ignored by all except LIN, TPP */ int i; /* Ignored by all except LIN, TPP */ #else long i; #endif Function-Based Metrics OverviewThese metrics are all calculated for the source code of an individual function. Some metrics are used in the definition of others, but there is no obvious sequence. They are described here in alphabetical order, for ease of reference. The complete list is as follows:
File-Based Metrics OverviewThese metrics are all calculated for the source code in a complete translation unit (TU). They are listed alphabetically here for convenience. They are not calculated in this sequence. because of their derivation from each other:
Class-Based Metrics OverviewThese metrics are calculated for the classes in your source code. To calculate some of these metrics you must also have run the relevant cross-module analysis program. See Cross-Module Analysis in Chapter 4: Analysing Source Code. Class-based metrics are described here in alphabetical order for ease of reference:
Project-Wide Metrics OverviewThese metrics are calculated once per complete project. The complete list is as follows:
Function-Based MetricsAKI - Akiyama's CriterionFunction-Based Metric This metric is the sum of the Cyclomatic complexity (CYC) and the number of function calls (SUB). Although this is not an independent metric, it is included on account of its use in documented case histories. See Akiyama[1] and Shooman[2] for more details. The metric is calculated as: [1] Akiyama, F. (1971) An Example of Software System Debugging, Proc. IFIP Congress 1971, Lbujlana, Yugoslavia, American Federation of information Processing Societies, Montvale, New Jersey. [2] Shooman, M.L. (1983) Software Engineering, McGraw-Hill, Singapore. AV1 - Average Size of Statement in FunctionAV2 - Average Size of Statement in FunctionAV3 - Average Size of Statement in FunctionAVx - Average Size of Statement in FunctionFunction-Based Metrics These metrics (AV1, AV2, and AV3) measure the average number of operands and operators per statement in the body of the function. They are calculated using Halstead's[1] calculations as follows: AVx = (N1 + N2) / number of statements in the function where: The AVx metrics are computed using ST1, ST2 and ST3 to represent the number of statements in a function. Hence there are three variants: AV1, AV2 and AV3 relating to the respective statement count metrics. This metric is used to detect components with long statements. Statements comprising a large number of textual elements (operators and operands) require more effort by the reader in order to understand them. This metric is a good indicator of the program's readability. Metric values are computed as follows: AV1 = (FN1 + FN2) / ST1 [1] Halstead, Maurice H. (1977). Elements of Software Science. BAK - Number of Backward JumpsFunction-Based Metric Jumps are never recommended and backward jumps are particularly undesirable. If possible, the code should be redesigned to use structured control constructs such as while or for instead of goto. Examplemain() { Backward: switch (n) { case 0: printf( stdout, "zero\n"); break; case 1: printf( stdout, "one\n"); goto Backward; /* 1 */ break; case 2: printf( stdout, "two\n"); break; default: printf( stdout, "many\n"); break; } } The above code sample has a BAK value of 1. CAL - Number of Functions Called from FunctionFunction-Based Metric This metric counts the number of function calls in a function. It differs from the metric SUB, in that only distinct functions are counted (multiple instances of calls to a particular function are counted as one call), and also that functions called via pointers are not counted. CYC - Cyclomatic ComplexityFunction-Based Metric Cyclomatic complexity is calculated as the number of decisions plus 1. High cyclomatic complexity indicates inadequate modularization or too much logic in one function. Software metric research has indicated that functions with a cyclomatic complexity greater than 10 tend to have problems related to their complexity. McCabe[1] gives an essential discussion of this issue as well as introducing the metric. Example 1int divide( int x, int y) { if (y != 0) /* 1 */ { return x / y; } else if (x == 0) /* 2 */ { return 1; } else { printf( "div by zero\n"); return 0; } } The above code sample has a cyclomatic complexity of 3 as there are two decisions made by the function. Note that correctly-indented code does not always reflect the nesting structure of the code. In particular, the use of the construct 'else if' always increases the level of nesting, but it is conventionally written without additional indentation and so the nesting is not visually apparent. Example 2void how_many(int n) { switch (n) { case 0: printf( "zero"); /* 1 */ break; case 1: printf( "one"); /* 2 */ break; case 2: printf( "two"); /* 3 */ break; default: printf( "many"); break; } } The above code sample has a cyclomatic complexity of 4, as a switch statement is equivalent to a series of decisions. Some metrication tools include use of the ternary operator ? : when calculating cyclomatic complexity. It could also be argued that use of the && and || operators should be included. Instead, CYC calculation is based on statements alone. CYC is one of the three standard metrics used by QAC for demographic analysis. [1] McCabe, T. J. (1976) A Complexity Measure, IEEE Transactions on Software Engineering, SE-2, pp. 308-320. ELF - Number of Dangling Else-IfsFunction-Based Metric This is the number of if-else-if constructs that do not end in an else clause. This metric is calculated by counting all if statements that do not have a corresponding else and for which QAC issues a warning 2004. ELF provides a quick reference allowing monitoring of these warnings. The code sample below has an ELF value of 1. Exampleint divide(int x, int y) { if (y != 0) { return x/y; } else if (x == 0) { return 1; } } FN1 - Number of Operator Occurrences in FunctionFunction-Based Metric This metric is Halstead's[1] operator count on a function basis (N1). FN1 is related to OPT and M21: all of these metrics count 'operators', the difference is summarized as:
See OPT for the definition of an operator. [1] Halstead, Maurice H. (1977). Elements of Software Science. FN2 - Number of Operand Occurrences in FunctionFunction-Based Metric This metric is Halstead's[1] operand count on a function basis (N2). FN2 is related to OPN and M20: all of these metrics count 'operands', the difference is summarized as:
See OPN for the definition of an operand. [1] Halstead, Maurice H. (1977). Elements of Software Science. GTO - Number of Goto statementsFunction-Based Metric Some occurrences of goto simplify error handling. However, they should be avoided whenever possible. According to the Plum Hall Guidelines, goto should not be used. HLB - Number of Halstead's delivered bugs (B)Function-Based Metric The metric is an estimate for the number of errors in the implementation.
Function Volume V = N * log2(n)
N - the number of all operators and operands within function n - the number of distinct operators and operands within function Exampleint foo( int i) /* Violation: B = 0.09 */ { int a; int b; a = i + (2 * (i + 1)) + (3 * (i + 2)); b = (i + (2 * (i + 1)) + (3 * (i + 2))) * 2; if (i >= 3) return a; if (i >= 2) return b; } [1] Halstead, Maurice H. (1977). Elements of Software Science. KDN - Knot DensityFunction-Based Metric This is the number of knots per executable line of code. The metric is calculated as: The value is computed as zero when XLN is zero. KNT - Knot CountFunction-Based Metric This is the number of knots in a function. A knot is a crossing of control structures, caused by an explicit jump out of a control structure either by break, continue, goto, or return. KNT is undefined for functions with unreachable code. This metric measures knots, not by counting control structure crossings, but by counting the following keywords:
The function below has an KNT value of 1. Examplevoid fn( int n, int array[], int key ) { while ( array[ n ] != key ) { if ( array[ n ] == 999 ) { break; } else { ++n; } } } LCT - Number of Local Variables DeclaredFunction-Based Metric This is the number of local variables of storage class auto, register, or static declared in a function. These are variables that have no linkage. The function below has an LCT value of 2. Exampleint other_result; extern int result; int test() { int x; /* 1 */ int y; /* 2 */ return (x + y + other_result); } LIN - Number of Code LinesFunction-Based Metric This is the total number of lines, including blank and comment lines, in a function definition between (but excluding) the opening and closing brace of the function body. It is computed on raw code. LIN is undefined for functions which have #include'd code or macros which include braces in their definition. The function below has an LIN value of 5. Exampleint fn() { int x; /* 1 */ int y; /* 2 */ /* 3 */ return (x + y); /* 4 */ /* Comment Here */ /* 5 */ } Long functions are difficult to read, as they do not fit on one screen or one listing page. An upper limit of 200 is recommended. LOP - Number of Logical OperatorsFunction-Based Metric This is the total number of logical operators (&&, ||) in the conditions of do-while, for, if, switch, or while statements in a function. The example function below has a LOP value of 2. Examplevoid fn( int n, int array[], int key ) { while (( array[n] != key ) && ( n > 0 )) { if (( array[n] == 999) || ( array[n] == 1000 )) { break; } else { ++n; } } } M07 - Essential Cyclomatic ComplexityFunction-Based Metric The essential cyclomatic complexity is obtained in the same way as the cyclomatic complexity but is based on a 'reduced' control flow graph. The purpose of reducing a graph is to check that the component complies with the rules of structured programming. A control graph that can be reduced to a graph whose cyclomatic complexity is 1 is said to be structured. Otherwise reduction will show elements of the control graph which do not comply with the rules of structured programming. The principle of control graph reduction is to simplify the most deeply nested control subgraphs into a single reduced subgraph. A subgraph is a sequence of nodes on the control flow graph which has only one entry and exit point. Four cases are identified by McCabe[1] which result in an unstructured control graph. These are:
However, if a subgraph possesses multiple entry or exit points then it cannot be reduced. The use of multiple entry and exit points breaks the most fundamental rule of structured programming. The example below has a M07 value of 4. Examplevoid g( int n, int pos, int force ) /* M07 = 4 Cannot reduce control graph to Cyclomatic Complexity of 1 */ { int nlines = 0; while ( --n >= 0 ) { pos = back_line( pos ); if ( pos == 0 ) { if ( ! force ) { break; } ++nlines; } } } [1] McCabe, T. J. (1976) A Complexity Measure, IEEE Transactions on Software Engineering, SE-2, pp. 308-320. M19 - Number of Exit PointsFunction-Based Metric This metric is a measure of the number of exit points in a software component and is calculated by counting the number of return statements. A function that has no return statements will have an M19 value of zero even though it will exit when falling through the last statement. This is regardless of whether the function is declared to have a return value or not (i.e. returns void). Calls to non-returning functions such as exit() or abort() are ignored by this metric. The example below has an M19 value of 3. Examplevoid f( int a ) { return; /* 1 */ if ( a ) return; /* 2 */ a++; return; /* 3 */ } M29 - Number of Functions Calling this FunctionFunction-Based Metric This metric is defined as the number of functions calling the designated function. The number of calls to a function is an indicator of criticality. The more a function is called, the more critical it is and, therefore, the more reliable it should be. MCC - Myer's IntervalFunction-Based Metric This is an extension to the cyclomatic complexity metric. It is expressed as a pair of numbers, conventionally separated by a colon. Myer's Interval is defined as CYC : CYC + L Cyclomatic complexity (CYC) is a measure of the number of decisions in the control flow of a function. L is the value of the QAC LOP metric which is a measure of the number of logical operators (&&, ||) in the conditional expressions of a function. A high value of L indicates that there are many compound decisions, which makes the code more difficult to understand. A Myer's interval of 10 is considered very high. The example below has a MCC value of 3:4 because the cyclomatic complexity is 3 and there is one connective (&&) used in the conditions. Exampleint divide( int x, int y) { if (y != 0) /* Condition 1 */ { return x / y; } else if (x == 0 && y > 2) /* Condition 2 */ /* Conditional expr 1 */ { return 1; } else { printf( "div by zero\n"); return 0; } } Note: In the calculation of MCC, the ternary operator (?:) is ignored. When exporting metric values or displaying in the Metrics Browser, rather than attempting to display a value pair, the value of L is chosen for MCC. MIF - Deepest Level of NestingFunction-Based Metric This metric is a measure of the maximum control flow nesting in your source code. You can reduce the value of this metric by turning your nesting into separate functions. This will improve the readability of the code by reducing both the nesting and the average cyclomatic complexity per function. The code example below has an MIF value of 3. Exampleint divide( int x, int y) { if (y != 0) /* 1 */ { return (x/y); } else if (x == 0) /* 2 */ { return 1; } else { printf( "Divide by zero\n"); while (x > 1) /* 3 */ printf( "x = %i", x); return 0; } } MIF is incremented in switch, do, while, if and for statements. The nesting level of code is not always visually apparent from the indentation of the code. In particular, an else if construct increases the level of nesting in the control flow structure but is conventionally written without additional indentation. MIF is one of the three standard metrics used by QAC for demographic analysis. PAR - Number of Function ParametersFunction-Based Metric This metric counts the number of declared parameters in the function argument list. Note that ellipsis parameters are ignored. PBG - Residual Bugs (PTH-based est.)Function-Based Metric Hopkins, in Hatton & Hopkins[1] investigated software with a known audit history and observed a correlation between Static Path Count (PTH) and the number of bugs that had been found. This relationship is expressed as PBG. PBG = log10 ( PTH ) [1] Hatton, L., Hopkins, T.R., (1989) Experiences With Flint, a Software Metrication Tool for Fortran 77, Symposium on Software Tools, Napier Polytechnic, Edinburgh, Scotland. PDN - Path DensityFunction-Based Metric This is a measure of the number of paths relative to the number of executable lines of code. PDN = PTH / XLN PTH - Estimated Static Program PathsFunction-Based Metric This is similar to Nejmeh's[1] NPATH statistic and gives an upper bound on the number of possible paths in the control flow of a function. It is the number of non-cyclic execution paths in a function. The NPATH value for a sequence of statements at the same nesting level is the product of the NPATH values for each statement and for the nested structures. NPATH is the product of:
Note: else and default are counted whether they are present or not. In switch statements, multiple case options on the same branch of the switch statement body are counted once for each independent branch only. For example: Exampleswitch( n ) { case 0: break; /* NPATH of this branch is 1 */ case 1: case 2: break; /* NPATH for case 1 & case 2 combined is 1 */ default: break; /* NPATH for this default is 1 */ } Since NPATH cannot be reliably computed if there are goto statements in the function, they are ignored by QAC for the purposes of calculating NPATH. The following code example has a static path count of 26. Exampleint n; if ( n ) { } /* block 1, paths 1 */ else if ( n ) { if ( n ) { } /* block 2, paths 1 */ else { } /* block 3, paths 1 */ /* block 4, paths block2+block3 = 2 */ switch ( n ) { case 1 : break; case 2 : break; case 3 : break; case 4 : break; default: break; } /* block 5, paths = 5 */ } /* block 6, paths block4*block5 = 10 */ else { if ( n ) { } /* block 7, paths 1 */ else { } /* block 8, paths 1 */ } /* block 9, paths block7+block8 = 2 */ /* block 10, paths block1+block6+block9 = 13 */ if ( n ) { } /* block 11, paths 1 */ else { } /* block 12, paths 1 */ /* block 13, paths block11+block12 = 2 */ /* outer block, paths block10*block13 = 26 */ Each condition is treated as disjoint. In other words, no conclusions are drawn about a condition that is tested more than once. The true path count through a function usually obeys the inequality: cyclomatic complexity <= true path count <= static path count Static path count is one of the three standard metrics used by QAC for demographic analysis. [1] Nejmeh, B.A. (1988), NPATH: A Measure of Execution Path Complexity and its Applications, Comm ACM, 31, (2), p. 188-200. RET - Number of Return Points in FunctionFunction-Based Metric RET is the count of the reachable return statements in the function, plus one if there exists a reachable implicit return at the } that terminates the function. The following example shows an implicit return: Examplevoid foo( int x, int y ) { printf( "x=%d, y=%d\n", x, y ); /* Here with implicit return. Hence RET = 1*/ } Structured Programming requires that every function should have exactly one entry and one exit. This is indicated by a RET value of 1. RET is useful when the programmer wants to concentrate on the functions that do not follow the Structured Programming paradigm. For example, those with switch statements with returns in many or every branch. ST1 - Number of Statements in FunctionST2 - Number of Statements in FunctionST3 - Number of Statements in FunctionSTx - Number of Statements in FunctionFunction-Based Metrics These metrics count the number of statements in the function body. There are 3 variants on the metric: ST1 is the base definition and counts all statements tabulated below. The following chart shows the statements counted by the STST metrics:
The following example shows statements counted by ST1, which for this function yields a value of 10: Examplevoid stst( void ) { int i; /* 1 */ label_1: /* 2 */ label_2: /* 3 */ switch ( 1 ) /* 4 */ { /* 5 */ case 0: /* 6 */ case 1: /* 7 */ case 2: /* 8 */ default: /* 9 */ break; /* 10 */ } } This metric indicates the maintainability of the function. Number of statements also correlates with most of the metrics defined by Halstead. The greater the number of statements contained in a function, the greater the number of operands and operators, and hence the greater the effort required to understand the function. Functions with high statement counts should be limited. Restructuring into smaller sub-functions is often appropriate. SUB - Number of Function CallsFunction-Based Metric The number of function calls within a function. Functions with a large number of function calls are more difficult to understand because their functionality is spread across several components. Note that the calculation of SUB is based on the number of function calls and not the number of distinct functions that are called, see CAL. A large SUB value may be an indication of poor design; for example, a calling tree that spreads too
rapidly. The following code example has an SUB value of 4. Exampleextern dothis(int); extern dothat(int); extern dotheother(int); void test() { int a, b; a = 1; b = 0; if (a == 1) { dothis(a); /* 1 */ } else { dothat(a); /* 2 */ } if (b == 1) { dothis(b); /* 3 */ } else { dotheother(b); /* 4 */ } } [1] Brandl, D.L. (1990), Quality Measures in Design, ACM Sigsoft Software Engineering Notes, vol 15, 1.
[x] Halstead, Maurice H. (1977). Elements of Software Science. UNR - Number of Unreachable StatementsFunction-Based Metric This metric is the count of all statements within the function body that are guaranteed never to be executed. UNR uses the same method for identifying statements as metric ST1. Hence UNR counts the following as statements if unreachable.
Statement Kind Counted
block simple statement followed by ; empty statement declaration statement label break continue do for goto if return switch while Example yielding UNR = 4 Examplevoid stunr( unsigned i ) { if ( i >= 0 ) { if ( i >= 0 ) { while ( i >= 0 ) return; } else /* unreachable */ { while ( i >= 0 ) return; } } return; /* unreachable */ } UNV - Unused or Non-Reused VariablesFunction-Based Metric An unused variable is one that has been defined but which is never referenced. A non-reused variable is a variable that has a value by assignment but which is never used subsequently. Such variables are generally clutter and are often evidence of 'software ageing', which is the effect of a number of programmers making changes. The code below has an UNV value of 2. Exampleint other_result; extern int result; int test() { int y; /* Unused */ int z; z = 1; /* Non-Reused */ return (result + other_result); } XLN - Number of Executable LinesFunction-Based Metric This is a count of lines in a function body that have code tokens. Comments, braces, and all tokens of declarations are not treated as code tokens. The function below has an XLN value of 9. Examplevoid fn( int n ) { int x; int y; if ( x ) /* 1 */ { x++; /* 2 */ for (;;) /* 3 */ /* Ignore comments */ /* Ignore braces */ { switch ( n ) /* 4 */ { case 1 : break; /* 5 */ case 2 : /* 6 */ case 3 : /* 7 */ break /* 8 */ ; /* 9 */ } } } } This metric is used in the computation of the KDN and PDN metrics.
File-Based MetricsBMO - Organic Programmer MonthsBMS - Semi-detached Programmer MonthsBME - Embedded Programmer MonthsFile-Based Metrics The COCOMO metrics are produced for each source code file. You can display an estimate of development costs for a whole project by choosing the COCOMO Cost Model option from the Reports menu. See Chapter 6: Reports for a discussion of the COCOMO cost model and an explanation of the three programming modes: Organic, Semi-detached, and Embedded. These metrics estimate the number of programmer-months required to create the source code in the respective environments. BME = 3.6 * ( TPP / 1000 ) 1.20 BUG - Residual Bugs (token-based estimate)File-Based Metric BUG = 0.001 * EFF2/3 This is an estimate of the number of bugs in the file, based on the number of estimated tokens. Its value would normally be lower than the sum of the function-based PBG values. For a more detailed discussion of software bug estimates, see Hatton and Hopkins,[1]. [1] Hatton, L., Hopkins, T.R., (1989) Experiences With Flint, a Software Metrication Tool for Fortran 77, Symposium on Software Tools, Napier Polytechnic, Edinburgh, Scotland. CDN - Comment to Code RatioFunction-Based Metric This metric is defined to be the number of visible characters in comments, divided by the number of visible characters outside comments. Comment delimiters are ignored. Whitespace characters in strings are treated as visible characters. A large value of CDN indicates that there may be too many comments, which can make a module difficult to read. A small value indicates that there may not be enough comments, which can make a module difficult to understand. The code below has 28 visible characters in comments and 33 visible characters in the code. The resulting CDN value is 0.85. Exampleint test() /* This is a test */ { int x; int y; /* This is another test */ return (x + y); } The value of CDN is affected by how QAC counts the comments. QAC can count comments in three possible ways:
DEV - Estimated Development (programmer-days)Function-Based Metric This is an estimate of the number of programmer days required to develop the source file. Unlike COCOMO statistics, which are based solely on the number of lines of code, this estimate is derived from the file's difficulty factor. It is a more accurate measure of the development time, especially after the scaling factor has been adjusted for a particular software environment. DEV = EFF / dev_scaling where dev_scaling is a scaling factor defined in qac.cfg. The default is 6000. DIF - Program DifficultyFunction-Based Metric This is a measure of the difficulty of a translation unit. An average C program has a difficulty of around 12. Anything significantly above this has a rich vocabulary and is potentially difficult to understand. DIF = VOL / ( (2 + VAR) * log2 (2 + VAR) ) ECT - Number of External Variables DeclaredFunction-Based Metric This is a measure of the number of data objects (not including functions) declared with external linkage. It is an indication of the amount of global data being passed between modules. It is always desirable to reduce dependence on global data to a minimum. Exampleextern int result; int other_result; main() { result = 20 + 30; other_result = result * 2; } The above code sample has an ECT value of 2. EFF - Programmer EffortFunction-Based Metric This metric is a measure of the programmer effort involved in the production of a translation unit. It is used to produce a development time estimate. FNC - Number of Functions in FileFunction-Based Metric This metric is a count of the number of function definitions in the file. FCO - Estimated Function CouplingFunction-Based Metric See Brandl[1]. Since the actual value of Brandl's metric requires a full, well-structured calling tree, FCO can only be an estimate. A high figure indicates a large change of complexity between levels of the calling tree. The metric is computed from FNC and the SUB values of the component functions in the translation unit: The code example below has an FCO value of 1 (2 - 2 + 1). ExampleBOOL isActive(CHANNEL c); BOOL okToRead(TEXTCHANNEL c) { return !isActive(c); } BOOL okToPrint(PRINTCHANNEL c) { return !isActive(c); } [1] Brandl, D.L. (1990), Quality Measures in Design, ACM Sigsoft Software Engineering Notes, vol 15, 1. HAL - Halstead Prediction of TOTFunction-Based Metric This metric and also ZIP are predictions derived from the vocabulary analysis metrics OPN and OPT of what the value of TOT should be. If they differ from TOT by more than a factor of 2, it is an indication of an unusual vocabulary. This usually means that either the source code contains sections of rather repetitive code or it has an unusually rich vocabulary. The two metrics are computed as follows: ZIP = ( OPN + OPT ) * ( 0.5772 + ln (OPN + OPT) ) M20 - Number of Operand OccurrencesFunction-Based Metric This metric is the number of operands in a software component and is one of the Halstead vocabulary analysis metrics. Halstead considered that a component is a series of tokens that can be defined as either operators or operands. Unlike OPN, this metric is the count of every instance of an operand in a file, regardless of whether or not it is distinct. OPN only counts the operands that are distinct. The code example below has a M20 value of 8. Examplevoid f( int a ) /* 1,2 -> f, a */ { if ( a > 1 ) /* 3,4 -> a, 1 */ { ++a; /* 5 -> a */ while ( a > 1 ) /* 6,7 -> a, 1 */ { --a; /* 8 -> a */ } } }
[x] Halstead, Maurice H. (1977). Elements of Software Science. M21 - Number of Operator OccurrencesFunction-Based Metric This metric is the number of operators in a software component and is one of the Halstead vocabulary analysis metrics. Halstead considered that a component is a series of tokens that can be defined as either operators or operands. Unlike OPT, this metric is the count of every instance of an operator in a file, regardless of whether or not it is distinct. OPT only counts the operators that are distinct. The code example below has a M21 value of 22. Examplevoid f( int a ) /* 1,2,3,4 -> void, (, int, ) */ { /* 5 -> { */ if ( a > 1) /* 6,7,8,9 -> if, (, >, ) */ { /* 10 ->{ */ ++a; /* 11,12 -> ++, ; */ while (a > 1) /* 13,14,15,16 -> while, (, >, ) */ { /* 17 -> { */ --a; /* 18,19 -> --, ; */ } /* 20 -> } */ } /* 21 -> } */ } /* 22 -> } */
[x] Halstead, Maurice H. (1977). Elements of Software Science. M22 - Number of StatementsFunction-Based Metric This metric is the number of statements in a software component. This is a count of semicolons in a file except for the following instances:
The code example below has a M22 value of 5. Examplevoid f( int a ) { struct { int i; int j; } ij; /* 1 */ a = 1; /* 2 */ a = 1; a = 2; /* 3,4 */ if ( a > 1 ) { return; /* 5 */ } } M28 - Number of Non-Header CommentsFunction-Based Metric This metric is a count of the occurrences of C or C++ style comments in a source file except for those that are within the header of a file. A file header is defined as tokens preceding the first code token or preprocessor directive token. M28 is based on the method used to compute CDN but differs from CDN in that CDN counts the visible characters within comments, whereas M28 counts the occurrences of comments. The code example below has a M28 value of 2. Example/* Header comment 1 Count = 0 */ /* Header comment 2 Count = 0 */ /* Last header comment Count = 0 code follows */ #define CENT 100 /* Non header comment Count = 1 */ void f( int a ) { /* Block scope comment Count = 2 */ a = CENT; return; } M33 - Number of Internal CommentsFunction-Based Metric This metric is a count of C style or C++ comments in a source file that are within functions or annotate a line of code at file scope. Comments within functions are all comments at block scope. Comments that annotate code are ones that start or end on the same line as code. M33 is based on the method used to compute CDN but differs from CDN in that CDN counts the visible characters within comments, whereas M33 counts the occurrences of comments. The code example below has a M33 value of 5. Example/* Header comment 1 Count = 0 */ /* Header comment 2 Count = 0 */ /* Last header comment Count = 0 code follows */ #define CENT 100 /* Annotating comment M33 = 1 */ int /* Annotating comment M33 = 2 */ i; /* Annotating comment M33 = 3 */ int j; /* Annotating comment M33 = 4 */ /* Non internal comment M33 = 0 */ void f( int a ) { /* Block scope comment M33 = 5 */ a = CENT; return; } OPN - Number of Distinct OperandsFunction-Based Metric This is the number of distinct operands used in the file. Distinct operands are defined as unique identifiers and each occurrence of a literal. Most literals, except 0 and 1, are usually distinct within a program. Since macros are usually used for fixed success and failure values (such as TRUE and FALSE), the differences in counting strategies are fairly minimal. The code below has an OPN value of 11. Exampleextern int result; /* 1 -> result */ static int other_result; /* 2 -> other_result */ main() /* 3 -> main */ { int x; /* 4 -> x */ int y; /* 5 -> y */ int z; /* 6 -> z */ x = 45; /* 7 -> 45 */ y = 45; /* 8 -> 45 */ z = 1; /* 9 -> 1 */ result = 1; /* 10 -> 1 */ other_result = 0; /* 11 -> 0 */ return (x + other_result); } OPT - Number of Distinct OperatorsFunction-Based Metric This covers any source code tokens not supplied by the user, such as keywords, operators, and punctuation. OPT is used in the calculation of a number of other metrics. The code below has an OPT value of 11. Exampleextern int result; /* 1,2,3 -> extern, int, ; */ static int other_result; /* 4 -> static */ main() /* 5,6 -> ()*/ { /* 7 -> { */ int x; int y; int z; x = 45; /* 8 -> = */ y = 45; z = 1; result = 1; other_result = 0; return (x + other_result); /* 9,10 -> return, + */ } /* 11 -> } */ SCT - Number of Static Variables DeclaredFunction-Based Metric This metric is computed as the number of variables and functions declared static at file scope. The code example below has an SCT value of 2. Examplestatic int other_result; /* 1 */ int result; static int test() /* 2 */ { int x; int y; int z; z = 1; return (x + other_result); } SHN - Shannon Information ContentFile-Based Metric Also known as the "entropy" H, this metric is a widely recognized algorithm for estimating the program space required to encode the functions in a source file. SHN is measured in bits and is calculated as follows: SHN = ZIP * log2 (√(OPN + OPT) + ln (OPN + OPT)) TDE - Embedded Total MonthsTDO - Organic Total MonthsTDS - Semi-detached Total MonthsFile-Based Metrics The COCOMO metrics are produced for each source code file. You can display an accurate estimate of development costs for a whole project by choosing the COCOMO Cost Model option from the Reports menu. Refer to Chapter 6: Reports for a discussion of the COCOMO cost model and an explanation of the three programming modes: Organic, Semi-detached, and Embedded. These metrics are a measure of elapsed time in months required to develop the source code in the respective environments. TDE = 2.5 * BME 0.32 TLN - Total Preprocessed Code LinesFile-Based Metric This metric is a count of the total number of source lines in the file after pre-processing. The pre-processed
file will reflect the processing of include files, pre-processor directives and the stripping of comment lines. See also TPP TOT - Total Number of Tokens UsedFile-Based Metric This metric is the total number of tokens, not distinct tokens, in the source file. The code example below has an TOT value of 19. Exampleint test() /* 1,2,3,4 */ { /* 5 */ int x; /* 6,7,8 int y; /* 9,10,11 */ /*Excluded Comment*/ return (x + y); /* 12,13,14,15,16,17,18 */ } /* 19 */ TPP - Total Unpreprocessed Code LinesFile-Based Metric This metric is a count of the total number of source lines in the file before pre-processing. See also TLN VAR - Total Number of VariablesFile-Based Metric The metric represents the total number of distinct identifiers. The code below has an VAR value of 5. Exampleint other_result; /* 1 */ extern int result; /* 2 */ int test() /* 3 */ { int x; /* 4 */ int y; /* 5 */ return (x + y + other_result); } VOL - Program VolumeFile-Based Metric This is a measure of the number of bits required for a uniform binary encoding of the program text. It is used to calculate various Halstead vocabulary metrics. The following is the calculation for the program volume: VOL = TOT * log 2 ( OPN + OPT )
[x] Halstead, Maurice H. (1977). Elements of Software Science. ZIP - Zipf Prediction of TOTFile-Based Metric ZIP = ( OPN + OPT ) * ( 0.5772 + ln (OPN + OPT) ) See HAL. CCA - Total Number of CharactersFile-Based Metric This metric is the total number of characters in the file. Only visible characters are counted, except in string or character literals, in which case all characters are counted and tabs are treated as one character. When counting comment characters, the comment delimiter characters are counted. CCB - Total Number of Code CharactersFile-Based Metric This metric is the total number of code characters in the file. Only visible characters are counted, except in string or character literals, in which case all characters are counted and tabs are treated as one character. Characters comprising comments are not counted, this includes the comment delimiter characters. CCC - Total Number of Comment CharactersFile-Based Metric This metric is the total number of visible comment characters in the file. The comment delimiter characters are not
counted.
Class-Based MetricsCBO - Coupling between objectsClass-Based Metric This is a count of the number of methods (member functions) or member objects of other classes accessed by a class. Only classes outside the inheritance hierarchy are considered because you would expect interaction with base and derived classes. Coupling to classes outside the inheritance tree should be viewed with suspicion as it makes the class less independent and less re-usable. This is one of Chidamber & Kemerer's[1] suite of object oriented metrics. [1] Chidamber, S.R. and Kemerer, C.F. (1991). Towards a Metrics Suite for Object Oriented Design, Proceedings of OOPSLA, '91, Sigplan notices, vol.26, no. 11, ACM Press. DIT - Deepest level of inheritanceClass-Based Metric This represents the number of derivations from the furthest base class down to this class. A high figure may indicate that the class depends on accumulated functionality, which makes understanding the class potentially difficult. This is one of the metrics defined by Chidamber & Kemerer. LCM - Lack of cohesion of methods within a classClass-Based Metric Methods within a class are partitioned into sets that access independent sets of member objects. The LCM metric is a count of sets of unrelated members for a type. For example (default): Exampleclass cohesive { private: int i1; int i2; int i3; public: void m1(int a) { i1 = a + i2; } void m2(int a) { i2 = a; } void m3(int a) { if (a > i3) ++i3; } }; This above code sample has a LCM value of 2 because methods m1 and m2 access the set of member objects i1 and i2. Method m3 accesses only i3 and can be viewed as independent of m1 and m2. (m3 is not cohesive with the other two methods). Similarly, where one method calls another, the set used for inclusion in LCM is the union of the sets from each method. Variations of the calculation of LCM may explicitly exclude constructors, so the default behavior of CMA is that constructors are excluded when calculating this metric. To include constructors, enable the configuration option "-ProdOption LCM::include_constructor". If constructors are included, and the constructor for such a type initializes all the members, then the value of LCM is guaranteed to be '1'. This is one of the metrics defined by Chidamber & Kemerer. MTH - Number of methods declared in classClass-Based Metric The number of methods declared within a class. This does not include methods declared in base classes. Classes with a large number of methods will be difficult to understand. NOC - Number of immediate childrenClass-Based Metric This is the number of classes for which this class is an immediate base class. A high figure for this metric indicates greater dependence on the functionality of this class, and more potential knock-on effects from changes to it. This is one of the metrics defined by Chidamber & Kemerer. NOP - Number of immediate parentsClass-Based Metric This metric indicates the number of inheritances for a class. Root base classes have a value of 0 while classes derived with a single inheritance have a value of 1. Classes that have multiple inheritance are prohibited by some programming standards. RFC - Response for classClass-Based Metric This indicates how functions can potentially be called directly from the class. The response set is calculated as the number of distinct class methods and functions called from the definitions of those methods. This is one of the metrics defined by Chidamber & Kemerer. WMC - Weighted methods per classClass-Based Metric This is the sum of cyclomatic complexities (See Cyclomatic complexity (CYC) in Function-Based
Metrics section of this document) for all the methods in the class. In the same way that cyclomatic complexity gives an
indication of the amount of testing required for an individual function, this metric gives an indication of the amount
of testing required for a whole class. This is one of the metrics defined by Chidamber & Kemerer.
Project-Wide MetricsNRA - Number of Recursions Across ProjectProject-Wide Metric This metric is defined to be the number of recursive paths in the call graph of the project's functions. A recursive path can be for one or more functions. The minimum, and often desirable, value of this metric is zero. Values greater than zero indicate the number of distinct loops in the call graph. NEA - Number of Entry points Across ProjectProject-Wide Metric This metric is the number of functions that are not called in the project. It is the number of nodes in the call graph which are not themselves called. For example, main() is not called; hence the minimum (build) value of 1. NFA - Number of Functions Across ProjectProject-Wide Metric This metric is the number of function definitions in the project. Note that this metric is the sum of QAC's FNC metric values for each source file included in the project. CYA - Cyclomatic Complexity Across ProjectProject-Wide Metric This metric is the sum of cyclomatic complexity values for each function definition in the project.
Note that this metric is the sum of QAC CYC metric values for each source file included in the
project.
GeneralThe Components of Function StructureFunction structure diagrams show the control flow structures within source code. Decisions (if, switch, and the condition part of a loop) are displayed as forks in the structure. The corresponding join, further to the right, indicates where the paths rejoin. Backward arcs, caused by loops, are shown as dotted lines. The function structure diagrams show the following code structures:
Each component progresses from the left to the right as the control flow would progress through the source code. Straight codestatic int code = 0; void funcStraight( void) { code = 1; } There is only one path through this particular function, which is represented as lying along the x-axis. Ifstatic int code = 0; void funcIf( void) { if (code > 0) { code = 1; } } This function has two paths. The first path is through the if statement and is shown by the raised line. The second path is where the if condition is false and is represented by the x-axis. If-Elsevoid funcIfElse( void) { if (code > 0) { code = 3; } else { code = 4; } } This function has two execution paths. The first path is the if sub-statement represented by the raised line. The second path is the else sub-statement represented by the x-axis. Note that the body of this structure is longer than the body of the if statement. If the two arms of the if-else were not straight line code, the body of the if branch would appear at the left hand end of the raised line and the body of the else branch would appear to the right of the lower line. Switchstatic int code = 0; void funcSwitch( void) { switch (code) { case 1: if (code == 1) { /* block of code */ } break; case 2: break; case 3: if (code == 3) { /* block of code */ } break; default: break; } } In the switch statement, the x-axis represents the default action, and each case statement is shown by a raised line. The two briefly raised lines represent the if statements within case 1 and case 3. The diagram shows how the if statements are staggered. The if of case 1 is shown on the left and the if of case 3 is shown on the right. While loopstatic int code = 0; void funcWhile( void) { while (code > 0) { --code; } } In the while loop, the x-axis represents the path straight through the function as though the while loop had not been executed. The solid raised line shows the path of the while body. The dotted line shows the loop to the beginning of the while statement. For loopstatic int code = 0; void doSomethingWith(int); void funcFor( void) { int i; for (i = 0; i > code; ++i) { doSomethingWith( i); } } The for loop is similar to the while loop as for loops can be rewritten as while loops. For example, funcFor in the above example could be written as: void funcFor( void) { int i = 0; while (i > code) { /* body */ ++i; } } Nested Structuresstatic int code = 0; void funcWhileIfElse( void) { while (code > 0) { if (code == 1) { code = 0; } else { code--; } } } This is an if-else contained within a while loop. The first solid raised line represents the while loop while the inner raised solid line represents the if-else loop. Other function structure components can be similarly nested. Break in a loopstatic int code = 0; void funcWhileIfBreak( void) { while (code > 0) { if (code == 3) { break; } code--; } if (code == 0) { code++; } } The break jumps to the end of the while statement and causes a knot. A knot is where the control flow crosses the boundary of another statement block and indicates unstructured code. In this case, break jumps to the end of the while statement and the next part of the program, the if statement, is executed. Return in a loopstatic int code = 0; void funcWhileIfReturn( void) { while (code > 0) { if (code == 3) { return; } code--; } if (code == 0) { code++; } } The return statement causes the program to jump to the end of the function. This jump breaks the control flow and causes a knot in the code. Continue in a loopstatic int code = 0; void funcWhileIfContinue( void) { while (code > 0) { if (code == 3) { continue; } code--; } if (code == 0) { code++; } } The continue statement causes the program to jump back to the beginning of the while loop. Unreachable code
static void funcUnReach( int i)
{
if (i)
{
i = 1;
}
goto Bad;
if (i)
{
i = 2;
}
Bad:
if (i)
{
i = 3;
}
return;
}
The raised red section represents code that is unreachable. The structure and approximate position of the unreachable code is shown. It occurs after the first if condition and before the last if condition. Its position above the main structure shows that the control flow misses the middle if condition. |