今回はかなり難しい話です。まずは、次のコードをご覧ください。
int main(void)
{
if (0xe-0xe)
puts("A");
else
puts("B");
return 0;
}
このプログラムをコンパイルし実行すると、出力されるのは "A" でしょうか? それとも "B" でしょうか? 実際に試してみることなく、どんな振る舞いになるかがわかった方は、C/C++ に相当詳しい方です。
答えは "A" でもなければ "B" でもありません。処理系のバグ、または独自拡張がない限り、このソースコードはコンパイルできません。実際に GCC で試してみると、
invalid suffix "-0xe" on integer constant
というエラーメッセージが出力されました。理由が分からないと、このエラーメッセージを見ても、何のことかさっぱり分かりませんね。
それでは順を追って説明することにしましょう。いわゆる「リンク」に相当する部分を除けば、C も C++ も、次の翻訳過程を経てコンパイルされます。
- 物理文字からソース文字集合への変換
- 物理行から論理行への変換
- 前処理字句と空白類文字の分解
- 前処理指令の実行
- ソース文字集合から実行文字集合への変換
- 文字列リテラルの連結
- 字句の並びに対する解析
このうち 1.~6. は、いわゆる「前処理(preprocess)」に相当する部分であり、6. を終えた時点でひとつの翻訳単位が完成することになります。そして、7. がいわゆる狭義の「コンパイル」に相当する部分です。
ここで、3. に登場する「前処理字句(preprocessing token)」に注目してみましょう。C/C++ では、ソースファイルに記述されたコードは「字句(token)」に分解されます。しかし、字句への分解はいきなり行われるのではなく、より大雑把な字句である前処理字句に分解された後、必要な箇所で前処理字句から字句に変換されます。前処理字句には、識別子、前処理数、区切り子と演算子がありますが、今回の問題に関係するのは、このうちの前処理数です。
ここまで分かれば、後は前処理数の構文を調べるだけで何が起こったのかが見えてきます。
pp-number ::=
digit
| '.' digit
| pp-number digit
| pp-number identifier-nondigit
| pp-number 'e' sign
| pp-number 'E' sign
| pp-number 'p' sign
| pp-number 'P' sign
| pp-number '.'
前処理数は、整数と浮動小数点数の区別も無ければ、10 進数、8 進数、16 進数の区別もありません。数字で始まるか、小数点で始まり、直後に数字が続くかすれば、空白類、区切り子、演算子以外が続く限り、前処理数の一部とみなされてしまうのです。
最初に例として挙げたコードを再びご覧ください。数字である 0 から始まり、 x, e, -, 0, x, e という前処理数の構成要素が続く 0xe-0xe は、翻訳過程 7. で整数値に変換されることになります。しかし、0xe まではよいものの、その後の -0xe は添え字(例えば long 型なら L を付けるといったもの)としては不正ですので、コンパイルエラーになってしまうのです。
トラックバック
ブックナビゲーション
- 技術情報
- Boost C++ Libraries メモ
- C++と組込み環境
- C++サンプル集
- C++テンプレート集
- C++プログラマのためのC言語入門
- C/C++迷信集
- [迷信] 'A'~'Z' の値は連続している
- [迷信] 0xe-0xe はゼロ
- [迷信] 1 バイトは 8 ビット
- [迷信] 2の累乗による割り算と右シフトは等価
- [迷信] FILE 型は構造体
- [迷信] abs は常に非負の値を返す
- [迷信] argv[0] はプログラム名
- [迷信] char 型は符号付き
- [迷信] double の出力書式は "%lf"
- [迷信] fflush で入力バッファをクリア
- [迷信] free でメモリを開放する
- [迷信] free に NULL を渡すとクラッシュする
- [迷信] gets は単純に fgets に置き換えられる
- [迷信] isalpha 関数の引数は char 型
- [迷信] new に失敗すると NULL が返る。
- [迷信] scanf ではバッファオーバーランを防げない
- [迷信] scanf でキーボードから入力
- [迷信] setjmp マクロの返却値は変数に代入できる
- [迷信] sizeof は定数式
- [迷信] void main(void)
- [迷信] とりあえず memset で初期化
- [迷信] アルゴリズム関数内で関数オブジェクトはコピーされない
- [迷信] オブジェクトの動的生成に失敗するとメモリリークする
- [迷信] コンストラクタから例外を送出してはならない
- [迷信] コンストラクタで自身をゼロクリア
- [迷信] コンパイラはプログラマの心を察してくれる
- [迷信] コンパイルエラーが出るのでアクセス指定子を修正
- [迷信] ソースコード中の即値を全廃せよ
- [迷信] ソースファイルの末尾に }
- [迷信] データ列のソートには qsort 関数を使うべし
- [迷信] プログラムは必ず main から始まる
- [迷信] 一重引用符の中には一文字しか書けない
- [迷信] 今どき int が 16 ビットの処理系なんて無い
- [迷信] 入力データ格納用配列のサイズは BUFSIZ
- [迷信] 割付けたメモリはプログラマが自分で解放しなければならない
- [迷信] 実数型とは浮動小数点型のことである
- [迷信] 引用符で囲んだヘッダ名はカレントディレクトリから探索する
- [迷信] 文字列から整数への変換には atoi
- [迷信] 構造体のタグ名は下線で始める
- [迷信] 構造体はクラスではない
- [迷信] 識別子に使える文字は英数字と下線のみ
- [迷信] 非局所オブジェクトは外部結合
- C99関数・マクロ・前処理スクリプト集
- C言語再入門
- C言語徹底入門
- Drupal メモ
- TOPPERS 情報
- ライブラリ開発入門
- 分割コンパイルをきわめる
- 擬似プロセッサを作る
- 車輪の再発明
- 過去の情報


毎回楽しく、拝見させてもらっています。 上
毎回楽しく、拝見させてもらっています。
上記のプログラムですが、BCCとVC++2008で実行してみたところ、エラーにならず、Bになりました。
試しにcygwinに入れていたgccでコンパイルしたところ、同じようにエラーになりました。
これは、gcc特有の現象ではないでしょうか。
未熟者ですので、変な指摘かもしれませんが。
コメントありがとうございます。
結論からいうと、Visual C++ 2008の不具合です。
詳細は、↓のコメントを参照してください。
http://d.hatena.ne.jp/kazuhooku/20091017/1255781430