Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

The C language provides several different kinds of constants: integer constants, such as 10 and 0x1C; floating constants, such as 1.0 and 6.022e+23; and character constants, such as 'a' and '\x10'. C also provides string literals, such as "hello, world" and "\n". These constants can all be referred to as literals.

...

The C programming language has several mechanisms for creating named, symbolic constants: const-qualified objects, enumeration constants, and object-like macro definitions. Each of these mechanisms has associated advantages and disadvantages.

const-Qualified Objects

Objects that are const-qualified have scope and can be type-checked by the compiler. Because these they are named objects (unlike macro definitions), some debugging tools can show the name of the object. The object also consumes memory.

...

Unfortunately, const-qualified objects cannot be used where compile-time integer constants are required, namely to define the

  • size Size of a bit-field member of a structure.
  • size Size of an array (except in the case of variable length arrays).
  • value Value of an enumeration constant.
  • value Value of a case constant.

If any of these are required, then an integer constant (which would be an rvalue) must be used.

const-qualified objects allow the programmer to take the address of the object.:

Code Block
const int max = 15;
int a[max]; /* invalidInvalid declaration outside of a function */
const int *p;

/* aA const-qualified object can have its address taken */
p = &max;

const-qualified objects are likely to incur some runtime overhead [Saks 2001b]. Most C compilers, for example, allocate memory for const-qualified objects. const-qualified objects declared inside a function body can have automatic storage duration. If so, the compiler will allocate storage for the object, and it will be on the stack. As a result, this storage will need to be allocated and initialized each time the containing function is invoked.

Enumeration Constants

Enumeration constants can be used to represent an integer constant expression that has a value representable as an int. Unlike const-qualified objects, enumeration constants do not consume memory. No storage is allocated for the value, so it is not possible to take the address of an enumeration constant.

Code Block
enum { max = 15 };
int a[max]; /* OK outside function */
const int *p;

p = &max; /* errorError: '"&'" on enum constant */

Enumeration constants do not allow the type of the value to be specified. An enumeration constant whose value can be represented as an int is always an int.

Object-like Macros

A preprocessing directive of the form

# define identifier replacement-list

defines an object-like macro that causes each subsequent instance of the macro name to be replaced by the replacement list of preprocessing tokens that constitute the remainder of the directive [ISO/IEC 9899:2011].

C programmers frequently define symbolic constants as object-like macros. For example, the code

...

Object-like macros do not consume memory, and, ; consequently, it is not possible to create a pointer to one. Macros do not provide for type checking because they are textually replaced by the preprocessor.

Macros can be passed as compile-time arguments.

Summary

The following table summarizes some of the differences between const-qualified objects, enumeration constants, and object-like macro definitions.

Method

Evaluated At

Consumes Memory

Viewable by Debuggers

Type Checking

Compile-Time Constant Expression

enumerations

Enumerations

compile

Compile time

no

No

yes

Yes

yes

Yes

yes

Yes

const-qualified

runtime

Runtime

yes

Yes

yes

Yes

yes

Yes

no

No

macros

Macros

preprocessor

Preprocessor

no

No

no

No

no

No

yes

Yes

Noncompliant Code Example

The meaning of the integer literal 18 is not clear in this example.:

Code Block
bgColor#ffcccc
langc
/* ... */
if (age >= 18) {
   /* Take action */
}
else {
  /* Take a different action */
}
/* ... */

...

This compliant solution replaces the integer literal 18 with the symbolic constant ADULT_AGE to clarify the meaning of the code.:

Code Block
bgColor#ccccff
langc
enum { ADULT_AGE=18 };
/* ... */
if (age >= ADULT_AGE) {
   /* Take action */
}
else {
  /* Take a different action */
}
/* ... */

...

Integer literals are frequently used when referring to array dimensions, as shown in this noncompliant code example.:

Code Block
bgColor#ffcccc
langc
char buffer[256];
/* ... */
fgets(buffer, 256, stdin);

This use of integer literals can easily result in buffer overflows , if, for example, the buffer size is reduced but the integer literal used in the call to fgets() is not.

...

In this noncompliant code example, the string literal "localhost" and integer constant 1234 are embedded directly in program logic and are consequently difficult to change.:

Code Block
bgColor#ffcccc
langc
LDAP *ld = ldap_init("localhost", 1234);
if (ld == NULL) {
  perror("ldap_init");
  return(1);
}

...

In this compliant solution, the host name and port number are both defined as object-like macros, so they can be passed as compile-time arguments.:

Code Block
bgColor#ccccff
langc
#ifndef PORTNUMBER     /* mightMight be passed on compile line */
#  define PORTNUMBER 1234
#endif

#ifndef HOSTNAME        /* mightMight be passed on compile line */
#  define HOSTNAME "localhost"
#endif

/* ... */

LDAP *ld = ldap_init(HOSTNAME, PORTNUMBER);
if (ld == NULL) {
  perror("ldap_init");
  return(1);
}

Exceptions

DCL06-C-EX1: Although replacing numeric constants with a symbolic constant is often a good practice, it can be taken too far. Remember that the goal is to improve readability. Exceptions can be made for constants that are themselves the abstraction you want to represent, as in this compliant solution.

...

Code Block
enum { TWO = 2 };     /* aA scalar */
enum { FOUR = 4 };    /* aA scalar */
enum { SQUARE = 2 };  /* anAn exponent */
x = (-b + sqrt(pow(b, SQUARE) - FOUR*a*c))/ (TWO * a);

...

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 such as in an array declaration) but not elsewhere (like such as in a loop through an array).

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

DCL06-C

low

Low

unlikely

Unlikely

medium

Medium

P2

L3

Automated Detection

Tool

Version

Checker

Description

LDRA tool suite
Axivion Bauhaus Suite

Include Page

LDRA

Axivion Bauhaus Suite_V

LDRA

Axivion Bauhaus Suite_V

201 S

Fully implemented

CertC-DCL06
Compass/ROSE

 

 



Could detect violations of this recommendation merely by searching for the use of "magic numbers" and magic strings in the code itself. That is, any number (except a few canonical numbers: −1, 0, 1, 2) that appears in the code anywhere besides where assigned to a variable is a magic number and should instead be assigned to a const integer, enum, or macro. Likewise, any string literal (except "" and individual characters) that appears in the code anywhere besides where assigned to a char* or char[] is a magic string

.

ECLAIR

Include Page
ECLAIR_V
ECLAIR_V
nomagicc

CC2.DCL06

Fully implemented

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C3120, C3121, C3122, C3123, C3131, C3132


LDRA tool suite
Include Page
LDRA_V
LDRA_V

201 S

Fully implemented

.PRQA QA-C

Parasoft C/C++test
Include Page
PRQA
Parasoft_V
PRQA
Parasoft_V

3120

3121

3122

3123

3131

3132

Partially implemented

CERT_C-DCL06-a

Use meaningful symbolic constants to represent literal values

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rec. DCL06-C


Checks for:

  • Hard-coded buffer size
  • Hard-coded loop boundary

Rec. fully covered.

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

Bibliography

[Henricson 1992]Chapter 10, "Constants
"[ISO/IEC 9899:2011]Section 6.3.2.1, "Lvalues, Arrays, and Function Designators," section 6.7, "Declarations,
"
section 6.7.2.2, "Enumeration Specifiers," and section 6.10.3, "Macro Replacement"
[Saks 2001a]
 

[Saks 2001b]
 

[Saks 2002]
 

[Summit 2005]Question 10.5b

...


...

Image Modified Image Modified Image Modified