Avoid the use of magic numbers in code when possible. Magic numbers are constant values that represent either an arbitrary value (such as a determined appropriate buffer size) or a malleable concept (such as the age a person is considered an adult, which could change between geopolitical boundaries). Rather, use appropriately named symbolic constants to clarify the intent of the code. In addition, if a specific value needs to be changed, reassigning a symbolic constant once is more efficient and less error prone than replacing every instance of the value.
Non-Compliant Code Example
The meaning of the numeric literal 18 is not clear in this example.
/* ... */ if (age >= 18) { /* Take action */ } else { /* Take a different action */ } /* ... */
Compliant Solution
The compliant solution replaces 18 with the symbolic constant ADULT_AGE
to clarify the meaning of the code.
When declaring immutable symbolic values, such as ADULT_AGE
, it is best to declare them as a constant in accordance with DCL00-A. Declare immutable values using enum or const.
enum { ADULT_AGE=18 }; /* ... */ if (age >= ADULT_AGE) { /* Take action */ } else { /* Take a different action */ } /* ... */
Non-Compliant Code Example
Magic numbers are frequently used when referring to array dimensions, as shown in this non-compliant coding example.
char buffer[256]; /* ... */ fgets(buffer, 256, stdin);
This use of magic numbers can easily result in buffer overflows, if for example, the buffer size is reduced but the magic number used in the call to fgets()
is not.
Compliant Solution (enum)
In this compliant solution the magic number is replaced with an enumeration constant (see DCL00-A. Declare immutable values using enum or const).
enum { BUFFER_SIZE=256 }; char buffer[BUFFER_SIZE]; /* ... */ fgets(buffer, BUFFER_SIZE, stdin);
Compliant Solution (((sizeof}})
A sizeof
expression can work just as well as an enumeration constant (see EXP09-A. Use sizeof to determine the size of a type or variable).
char buffer[256]; /* ... */ fgets(buffer, sizeof(buffer), stdin);
Using the sizeof
expression reduces the total number of names declared in the program, which is almost always a good thing to do.
Exceptions
DCL06-EX1: While replacing numeric constants with a symbolic constant is often a good practice, it can be taken too far. Exceptions can be made for constants that are themselves the abstraction you want to represent, as in this compliant solution.
x = (-b + sqrt(b*b - 4*a*c)) / (2*a);
Replacing numeric constants with symbolic constants in this example does nothing to improve the readability of the code, and may in fact make the code more difficult to read:
enum { TWO = 2 }; /* a scalar */ enum { FOUR = 4 }; /* a scalar */ enum { SQUARE = 2 }; /* an exponent */ x = (-b + sqrt(pow(b, SQUARE) - FOUR*a*c))/ (TWO * a);
When implementing recommendations, it is always necessary to use sound judgment.
Risk Assessment
Using numeric literals makes code more difficult to read and understand. Buffer overruns are frequently a consequence of a magic number being changed in one place (like an array declaration) but not elsewhere (like a loop through an array).
Recommendation |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
DCL06-A |
1 (low) |
1 (unlikely) |
2 (medium) |
P2 |
L3 |
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
[[Henricson 92]] Chapter 10, "Constants"
[[ISO/IEC 9899-1999]] Section 6.7, "Declarations"
DCL05-A. Use typedefs to improve code readability 02. Declarations and Initialization (DCL) DCL07-A. Include the appropriate type information in function declarators