It is necessary to understand how macro replacement works in C, particularly in the context of concatenating tokens using the ## operator and converting macro parameters to strings using the # operator.
Concatenating Tokens
The {{Wiki Markup ##}} preprocessing operator is used to merge two tokens into one while expanding macros. This is called token pasting or token concatenation. When a macro is expanded, the two tokens on either side of each ## operator are combined into a single token, which replaces the {{##}} and the two original tokens in the macro expansion \[[FSF 05|AA. C References#FSF 05]\macros, which is called token pasting or token concatenation. When a macro is expanded, the two tokens on either side of each ## operator are combined into a single token that replaces the ## and the two original tokens in the macro expansion [FSF 2005].
Token pasting is most useful when one or both of the tokens comes come from a macro argument. If either of the tokens next to an a ## is a parameter name, it is replaced by its actual argument before ## executes. The actual argument is not macro - expanded first.
Stringification
Parameters are not replaced inside string constants, but you can use the {{Wiki Markup #}} preprocessing operator can be used instead. When a macro parameter is used with a leading {{#}}, the preprocessor replaces it with the literal text of the actual argument , converted to a string constant \ [[FSF 05|AA. C References#FSF 05]\].
Non-Compliant Code Example
FSF 2005].
Noncompliant Code Example
The following definition for The following definition for {{Wiki Markup static_assert()}} from \[[DCL03-AC. Use a static assertion to test the value of a constant expression]] uses the {{JOIN()}} macro to concatenate the token {{assertion_failed_at_line_}} with the value of {{\_\_LINE\_\_}}. :
| Code Block |
|---|
#define static_assert(e) \ typedef char JOIN(assertion_failed_at_line_, __LINE__) \ [(e) ? 1 : -1] |
{{\Wiki Markup _\_LINE\_\_}} is a predefined macro names which expands to an integer constant representing the presumed line number of the current source line within the current source file \[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]\].macro name that expands to an integer constant representing the presumed line number of the current source line within the current source file. If the intention is to expand the __LINE__ macro, which is likely the case here, the following definition for JOIN() is non-compliant:
| Code Block | ||
|---|---|---|
| ||
#define JOIN(x, y) x ## y
|
noncompliant because the __LINE__ is not expanded, and the character array is subsequently named assertion_failed_at_line___LINE__.
...
:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#define JOIN(x, y) x ## y
|
Compliant Solution
To get the macro to expand, a second level of indirection is required, as shown by this compliant solution:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#define JOIN(x, y) JOIN_AGAIN(x, y)
#define JOIN_AGAIN(x, y) x ## y
|
JOIN(x, y) calls JOIN_AGAIN(x, y) so that , if x or y is a macro, they are it is expanded before the ## operator pastes them together.
Note also that macro parameters cannot be individually parenthesized when concatenating tokens using the {{Wiki Markup ##}} operator, converting macro parameters to strings using the {{#}} operator, or concatenating adjacent string literals. This is an exception *, PRE01-C-EX2* to \[[, to PRE01-AC. Use parentheses within macros around parameter names]\].
...
Noncompliant Code Example
This example is non-compliant noncompliant if the programmer's intent is to expand the macro before stringification:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#define str(s) #s
#define foo 4
str(foo)
|
The macro invocation str(foo) expands to "foo".
Compliant Solution
To stringify the result of expansion of a macro argument, you must use two levels of macros must be used:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#define xstr(s) str(s)
#define str(s) #s
#define foo 4
|
The macro invocation xstr(foo) expands to "4". This is because 's' is stringified when it is used in str(), so it is not macro - expanded first. However, 's' is an ordinary argument to xstr(), so it is completely macro - expanded before xstr() is expanded. Consequently, by the time str() gets to its argument, it has already been macro - expanded.
Risk Assessment
Recommendation | Severity | Likelihood |
|---|
Detectable | Repairable | Priority | Level |
|---|---|---|---|
PRE05- |
low
unlikely
medium
P1
C | Low | Unlikely | No | Yes | P2 | L3 |
Automated Detection
| Tool | Version | Checker | Description | ||||||
|---|---|---|---|---|---|---|---|---|---|
| Axivion Bauhaus Suite |
| CertC-PRE05 | |||||||
| CodeSonar |
| LANG.PREPROC.HASH | Macro uses # operator Macro argument is both mixed and expanded Macro uses ## operator | ||||||
| Helix QAC |
| C0341, C0342, C0801, C0802, C0803, C0811, C0872, C0880, C0881, C0884 | |||||||
| Klocwork |
| MISRA.DEFINE.SHARP.ORDER.2012 | |||||||
| LDRA tool suite |
| 76 S, 125 S, 637 S | Enhanced Enforcement | ||||||
| Polyspace Bug Finder |
| Checks for incorrectly expanded macros | |||||||
| PC-lint Plus |
| 9024 | Assistance provided: reports any use of pasting or stringizing operators in a macro definition |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
...
Related Guidelines
| SEI CERT C++ Coding Standard | VOID PRE05-CPP. Understand macro replacement when concatenating tokens or performing stringification |
Bibliography
| [FSF 2005] | Section 3.4, |
...
| " |
...
| Stringification" Section 3.5, |
...
| " |
...
...
|http://gcc.gnu.org/onlinedocs/gcc-4.3.0/cpp/Concatenation.html#Concatenation]" \[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]\] Section 6.10.3, "Macro replacement," Section 6.10.3.3, "The {{##}} operator," Section 6.10.3.2, "The {{#}} operator," Section 6.10.3.4, "Rescanning and further replacement," and Section 6.10.8, "Predefined macro names" \[[Saks 08|AA. C References#Saks 08]\] Dan Saks, Stephen C. Dewhurst. Presentation. Sooner Rather Than Later: Static Programming Techniques for C++.PRE04-A. Do not reuse a standard header file name 01. Preprocessor (PRE) PRE06-A. Enclose header file in an inclusion guard