てきとうなメモ

本の感想とか技術メモとか

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).だいたい以下のようなかんじ.

  1. cpp_get_tokenでトークンを取得(libcpp/expr.c)
    1. _cpp_lex_tokenを呼ぶ(libcpp/macro.c)
    2. _cpp_lex_directを呼ぶ(libcpp/lex.c)
    3. 最初の文字が[0-9]ならばlex_numberを呼ぶ
    4. [_a-zA-Z0-9\.]のどれかの文字か,[eEpP][+-]の文字列が続けば,数字っぽいトークンとして文字列をコピー
  2. 数値とか文字とかだったらeval_token
    1. 数値だったらcpp_classify_number
    2. 浮動小数点数だったら浮動小数点数,整数だったら整数として解析する.
    3. 0xe-0xeは0xで16進と判断されてその場合は浮動小数点数と認識されない.eが16進の数値として解析されるがそれ以上は[0-9a-fA-F]ではないので,integerのsuffixと見なされるのだが,解析できずに"invalid suffix ... on integer constant"と出力する.

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);
}