PRE05-C. 字句の結合や文字列化を行う際のマクロ置換動作をよく理解する
C言語でのマクロ置換動作、特に、##
演算子による字句結合や、#
演算子によるマクロの引数を文字列に変換する場合の動作をよく理解すること。
字句の結合
##
前処理演算子を使い、マクロ展開時に二つの字句を一つに結合することができる。これは字句貼り付け(token pasting)あるいは字句連結と呼ばれる。マクロが展開されるとき、各 ##
演算子の両側にある二つの字句を一つの字句に結合した文字列で、##
および二つのオリジナルの字句を置き換える[FSF 2005]。
字句貼り付けが最も便利なのは、字句の一方もしくは両方がマクロ引数によって与えられる場合である。##
に並ぶ字句が仮引数名であったなら、仮引数名を実引数名に置き換えて ##
が実行される。実引数が最初にマクロ展開されるわけではない。
文字列化
文字列定数を構成する文字の並びはマクロ置換されないが、#
前処理演算子を使うことで同様の処理を実装できる。マクロ定義において仮引数の先頭に #
が付いていた場合、プリプロセッサはそれを実引数名の文字列定数に置き換える[FSF 2005]。
違反コード
「DCL03-C. 定数式の値をテストするには静的アサートを使う」からとってきた以下の定義は、JOIN()
マクロを使って字句 assertion_failed_at_line_
と __LINE__
の値を連結している。
#define static_assert(e) \ typedef char JOIN(assertion_failed_at_line_, __LINE__) \ [(e) ? 1 : -1]
__LINE__
はあらかじめ定義されたマクロ名であり、現在のソースファイル中の推定される行番号を表す整数定数に展開される。プログラマの意図が __LINE__
マクロを展開することであるなら(ここではそうである可能性が高いが)、以下のような JOIN()
の定義は違反コードである。__LINE__
は展開されず、結果的に assertion_failed_at_line___LINE__
という名前の文字配列ができる。
#define JOIN(x, y) x ## y
適合コード
マクロを適切に展開するには、以下の適合コードに示すように、二段階の回り道が必要である。
#define JOIN(x, y) JOIN_AGAIN(x, y) #define JOIN_AGAIN(x, y) x ## y
JOIN(x, y)
は JOIN_AGAIN(x, y)
に展開される。x
や y
が実際にはマクロであるならば、それらが先に展開されてから、##
演算子がその結果をひとつに貼りつけることになる。
##
演算子を使って字句を連結する場合、#
演算子を使ってマクロ引数を文字列に変換する場合、そして隣接する文字列リテラルを結合する場合には、マクロ引数を個々に括弧で囲むことはできないことに注意。これは、「PRE01-C. マクロ内の引数名は括弧で囲む」の例外 PRE01-EX2 に該当する。
違反コード
プログラマの意図が、文字列化を行う前にマクロを展開することであったならば、これは違反コードである。
#define str(s) #s #define foo 4 str(foo)
マクロ呼び出し str(foo)
は foo
に展開される。
適合コード
引数を先にマクロ展開してからその結果を文字列化するには、二段階のマクロを使用しなければならない。
#define xstr(s) str(s) #define str(s) #s #define foo 4
マクロ呼び出し xstr(foo)
は 4
に展開される。xstr()
マクロ定義のなかでは #
や ##
は使われていないので, xstr()
のマクロ展開時には引数 s
もマクロ展開され、その後に str()
が展開される時点ではその引数はすでに 4 にマクロ展開されているのである。
リスク評価
レコメンデーション |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
PRE05-C |
低 |
低 |
中 |
P2 |
L3 |
自動検出(最新の情報はこちら)
ツール | バージョン | チェッカー | 説明 |
---|---|---|---|
LDRA tool suite |
V. 8.5.4 |
125 S |
実装済み |
関連するガイドライン
CERT C++ Secure Coding Standard | PRE05-CPP. Understand macro replacement when concatenating tokens or performing stringification |
参考資料
[FSF 2005] | Section 3.4, "Stringification" Section 3.5, "Concatenation" |
[Saks 2008] |
翻訳元
これは以下のページを翻訳したものです。
PRE05-C. Understand macro replacement when concatenating tokens or performing stringification (revision 71)