cpp-operators - Cプリプロセッサで利用可能な演算子を把握する

目的

Cプリプロセッサで、#if の行内で使える演算子がなにかを、漏れなく把握したい。
K&Rやウェブ検索では、適切に見つけられなかった。

結果

以下の演算子が使用可能(gcc-3.4.6の場合):

演算子 結合規則 備考
defined X (不明) (Xの前後に一重のみカッコ付加可能、優先順位は不明)
() 左から右
! ~ + - 右から左 (単項演算子
* / % 左から右
+ - 左から右
<< >> 左から右
< <= > >= 左から右
== != 左から右
& 左から右
^ 左から右
左から右
&& 左から右
|| 左から右
?: 右から左
, 左から右

C言語内で使える演算子

d:id:taiyo:20080203#p1 を参照。
これに加えて、プリプロセッサで独自に使える演算子としては、これ以外に defined がある。
では、プリプロセッサで使えない演算子は?

明らかに使えないだろうという演算子

プリプロセッサには変数がないので、ポインタも配列も構造体もない(ただし関数マクロはある)。
これから、以下の演算子はあきらかに使えないであろう。

演算子 結合規則 備考 使えない理由
[] -> . 左から右 配列・ポインタ・構造体へのアクセス
++ -- * & (type) 右から左 (単項演算子 変数・ポインタの操作
= += -= *= /= %= &= ^= |= <<= >>= 右から左 変数への代入

残りは実際に試してみる

以下の演算子を、実際にプリプロセッサを動作させて試す。

演算子 結合規則 備考
defined X (不明) (優先順位は不明)
() 左から右
! ~ + - sizeof 右から左 (単項演算子
* / % 左から右
+ - 左から右
<< >> 左から右
< <= > >= 左から右
== != 左から右
& 左から右
^ 左から右
左から右
&& 左から右
|| 左から右
?: 右から左
, 左から右

動作確認用に、以下を実行した。

operators.sh
https://sssvn.jp/svn/spikelet/c/cpp-if-parser/spec-check/operators.sh
#!/bin/sh
TMPSRC=check.c
TMPLOG=compile.log
trap 'rm -f $TMPSRC $TMPLOG' 0

while read line; do
  echo -n "CHECK: '$line' ..."
  cat > $TMPSRC <<EOF
#if $line
int a = 0;
#endif
EOF
  if gcc -E $TMPSRC 2>$TMPLOG >/dev/null; then
    echo "OK"
  else
    echo -n "failed:" 
    sed -n '1s/^[^:]*:[^:]*:[^:]*://p' $TMPLOG
  fi
done <<EOF
defined X
defined(X)
defined ( X ) 
defined()X
defined(defined(X))
defined(defined)
defined((X))
defined(((X)))
sizeof(int)
sizeof int
+1
-1
~1
!1
1 * 1
1 / 1
1 % 1
1 + 1
1 - 1
1 << 1
1 >> 1
1 < 1
1 <= 1
1 > 1
1 >= 1
1 == 1
1 != 1
1 & 1
1 ^ 1
1 | 1
1 && 1
1 || 1
1 ? 1 : 0
1, 1
EOF

これを、gcc-3.4.6で実行した結果は以下:

CHECK: 'defined X' ...OK
CHECK: 'defined(X)' ...OK
CHECK: 'defined ( X )' ...OK
CHECK: 'defined()X' ...failed: operator "defined" requires an identifier
CHECK: 'defined(defined(X))' ...failed: missing ')' after "defined"
CHECK: 'defined(defined)' ...OK
CHECK: 'defined((X))' ...failed: operator "defined" requires an identifier
CHECK: 'defined(((X)))' ...failed: operator "defined" requires an identifier
CHECK: 'sizeof(int)' ...failed: missing binary operator before token "("
CHECK: 'sizeof int' ...failed: missing binary operator before token "int"
CHECK: '+1' ...OK
CHECK: '-1' ...OK
CHECK: '~1' ...OK
CHECK: '!1' ...OK
CHECK: '1 * 1' ...OK
CHECK: '1 / 1' ...OK
CHECK: '1 % 1' ...OK
CHECK: '1 + 1' ...OK
CHECK: '1 - 1' ...OK
CHECK: '1 << 1' ...OK
CHECK: '1 >> 1' ...OK
CHECK: '1 < 1' ...OK
CHECK: '1 <= 1' ...OK
CHECK: '1 > 1' ...OK
CHECK: '1 >= 1' ...OK
CHECK: '1 == 1' ...OK
CHECK: '1 != 1' ...OK
CHECK: '1 & 1' ...OK
CHECK: '1 ^ 1' ...OK
CHECK: '1 | 1' ...OK
CHECK: '1 && 1' ...OK
CHECK: '1 || 1' ...OK
CHECK: '1 ? 1 : 0' ...OK
CHECK: '1, 1' ...OK

gcc-4.2.3でも同じ結果。

つまり、definedはカッコ無しかカッコ1つのみが使用可能。sizeofは(かっこ付きでもかっこ無しでも)使えない。それ以外はすべて使用可能。

追記:preprocessor内でsizeofが使えないという話は、C FAQ(Question 10.13)にも書いてあった。