C言語における空白文字
C言語における空白文字は、文字列の一部分としてと、トークンを明示的に区切る (long long
と longlong
は違う) こと以外には基本的に無意味というように思える。しかしプリプロセッサレベルでは、以下の場面で意味がある。
includeの中身
通常 <stdio.h>
のようなコード断片は <
stdio
.
h
>
に分解されるが、includeディレクティブ(または一部のpragma)の中でのみ特別に <stdio.h>
という1つのトークンとして認識される (C11/§6.4.7)。このトークンに \
, //
, /*
, '
, "
を含めた場合の挙動は未定義だが、空白を入れることは許されている (C11/§6.4.7)。したがって、
#include <a b c>
においては(規格書を読む限りは)空白文字が意味を持つ。
ただし、Cの規格では、includeのファイル名の解釈自体が処理系定義であり (C11/§6.10.2)、規格で規定されているヘッダ名に空白はない (C11/§7.1.2)。
defineの直後
以下の2つは異なる意味を持つ。
#define f(x, y) (y, x) #define f (x, y) (y, x)
試しに以下のように実行してみるとわかる。
$ gcc -E - #define f(x, y) (y, x) #define g (x, y) (y, x) f(1, 2) g(1, 2) # 1 "<stdin>" # 1 "<built-in>" # 1 "<command-line>" # 31 "<command-line>" # 1 "/usr/include/stdc-predef.h" 1 3 4 # 32 "<command-line>" 2 # 1 "<stdin>" (2, 1) (x, y) (y, x)(1, 2)
このように、Cの規格では「マクロ関数定義の場合は、マクロ名と括弧の間に空白を入れない」と明確に規定する (C11/§6.10) ことで、関数マクロと通常のマクロを区別している。
文字列化
マクロの #
で文字列化したときには、空白の存在/非存在が保たれる。(C11/§6.10.3.2)
$ gcc -E - #define str2(x) #x #define str(x) str2(x) str(1+1) str(1 + 1) str(1 + 1) str(1 /* hoge */ + /* fuga */ 1) # 1 "<stdin>" # 1 "<built-in>" # 1 "<command-line>" # 31 "<command-line>" # 1 "/usr/include/stdc-predef.h" 1 3 4 # 32 "<command-line>" 2 # 1 "<stdin>" "1+1" "1 + 1" "1 + 1" "1 + 1"
コメントを空白文字に置き換えることは、規格で明示されている (C11/§5.1.1.2) 。複数の空白を1つの空白文字にまとめるかどうかは、処理系定義である (C11/§5.1.1.2) 。 ただし、文字列化されるさいは、どの種類の空白も空白文字に置き換えられる (C11/§6.10.3.2)。
まとめ
C言語のコードは trigraph処理→改行処理→PP字句解析→プリプロセス→真正な字句への変換→構文解析→…… という流れで処理される (C11/§5.1.1.2)。ただでさえプリプロセスと構文解析の2段階の処理があってややこしいが、プリプロセスで使われる字句データと構文解析で使われる字句データに微妙に違いがある点も見落とせない。空白はその例のひとつで、構文解析では意味をもたないがプリプロセスでは意味をもつ。