0xe-0xe
Cの仕様(pdf)の6.4.2や6.4.8を見ると
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 . identifier-nodigit: nodigit niversal-character-name other implementation-defined characters nondigit: one of [_a-zA-Z]
ってなっているので,"0xe-0xe"はpp-numberとみなされるのが正しいっぽい.コメント欄に書かれてある通りの話だけども.
gccの処理はlibcpp/expr.cの_cpp_parse_exprのあたりを読んでみるとなんとなくわかる(チェックしたバージョンは4.2.1).だいたい以下のようなかんじ.
- cpp_get_tokenでトークンを取得(libcpp/expr.c)
- _cpp_lex_tokenを呼ぶ(libcpp/macro.c)
- _cpp_lex_directを呼ぶ(libcpp/lex.c)
- 最初の文字が[0-9]ならばlex_numberを呼ぶ
- [_a-zA-Z0-9\.]のどれかの文字か,[eEpP][+-]の文字列が続けば,数字っぽいトークンとして文字列をコピー
- 数値とか文字とかだったらeval_token
tccだとエラーが発生しなかったという話なんだけども,最新版(0.9.25)ではエラーがでますね.
$ tcc -v tcc version 0.9.25 $ cat hoge.c #include<stdio.h> int main(void) { if (0xe-0xe) puts("A"); else puts("B"); return 0; } $ gcc hoge.c hoge.c:4:7: error: invalid suffix "-0xe" on integer constant $ tcc hoge.c hoge.c:4: invalid number
tccは構文エラーのチェックが甘いかもしれない.以下のようなコードも通ってしまったし.
$ cat sample.c int main() { printf("%d\n", 0x); } $ gcc sample.c sample.c: In function ‘main’: sample.c:2: warning: incompatible implicit declaration of built-in function ‘printf’ sample.c:2:18: error: invalid suffix "x" on integer constant $ tcc sample.c $ ./a.out 0
(追記)
やっぱり,tcc-0.9.24のチェックが甘かったっぽい.
tcc-0.9.24とtcc-0.9.25とでparse_number(pp-numberを解析して値を計算する関数)のdiffをとってみると,以下の部分が0.9.25で追加されている
if (ch) error("invalid number\n");
これは本当にpp-numberの最後までスキャンしたかをチェックする.逆に言うと0.9.24ではこのチェックをしていないため,途中までスキャンした文字列がCの数値リテラルとして正しければコンパイルエラーにならないことがある.その結果,以下のようなコードもコンパイルエラーにならないはず.チェックする環境が手元にないのでチェックはしていないけど
int main() { printf("%d\n", 123hoge); printf("%d\n", 0xffhoge); printf("%f\n", 123e-2hoge); }