web-dev-qa-db-ja.com

16進数と10進数の単純な加算を記述するための規則

オールドタイマーですが、Cでの定数の解析を(もう)完全に把握していないのではないかと心配しています。次の1ライナーの2番目はコンパイルに失敗します。

int main( void ) { return (0xe +2); }
int main( void ) { return (0xe+2); }

$ gcc -s weird.c

weird.c: In function ‘main’:
weird.c:1:28: error: invalid suffix "+2" on integer constant
int main( void ) { return (0xe+2); }
                           ^

コンパイルが失敗する理由は、おそらく0xe + 2がC11標準節6.4.4.2に従って16進浮動小数点定数として解析されるためです。私の質問は、Cで16進数と10進数の単純な加算を書き込むために、conventionが存在するかどうかです。構文解析では、空白に依存する必要はありません。

これは、gccバージョン5.4.0 20160609(Ubuntu 5.4.0-6ubuntu1〜16.04.9)でした。前処理(-E)後にコンパイルを停止すると、コンパイルの失敗がcppではなくgccで発生することがわかります。

23
Baard

私の質問は、Cで16進数と10進数の単純な加算を書くための規則が存在するかどうかです。

慣例では、スペースを使用します。これは実際にはC116.4§3で義務付けられています。

前処理トークンは空白で区切ることができます。これは、コメント(後述)、空白文字(スペース、水平タブ、改行、垂直タブ、およびフォームフィード)、あるいはその両方で構成されます。

プレーンスペースが一般的に使用される場所です。

同様のエキゾチックな問題が言語のあちこちに存在します、いくつかの例:

  • _---a_は_- --a_と書き直す必要があります。
  • _a+++++b_は_a++ + ++b_と書き直す必要があります。
  • _a /// comment_
    _b;_
    次のように書き直す必要があります
    _a / // comment_
    b

等々。これらすべての場合の原因は、いわゆる「最大ムンクルール」、C116.4§4に従うトークンパーサーです。

入力ストリームが特定の文字までの前処理トークンに解析されている場合、次の前処理トークンは、前処理トークンを構成できる最長の文字シーケンスです。

この特定のケースでは、プリプロセッサは、pp-numberと呼ばれる前処理トークンを構築するときに、浮動小数点定数と整数定数を区別しません。 C11 6.4.8:

pp-numbere記号
pp-number[〜#〜] e [〜#〜]記号
pp-numberp記号
pp-number[〜#〜] p [〜#〜]記号
pp-number

前処理番号は、オプションでピリオド(。)が前に付いた数字で始まり、有効な識別子文字と文字シーケンスe +、e-、E +、E-、p +、p-、P +、またはP-が続く場合があります。

ここで、pp-numberは、プリプロセッサに関する限り、浮動小数点定数である必要はないようです。


(補足として、文字列内の16進エスケープシーケンスを終了する場合にも同様の規則が存在します。たとえば、文字列_"ABBA"_を新しい行に出力したい場合、書き込むことはできません。

puts("\xD\xABBA");(CR + LF + string)

この場合の文字列は、16進エスケープシーケンスの一部として解釈される可能性があるためです。代わりに、空白を使用してエスケープシーケンスを終了し、プリプロセッサ文字列の連結に依存する必要があります:puts("\xD\xA" "BBA")。目的は同じで、プリプロセッサにコードの解析方法をガイドします。 )

13
Lundin

GCCは0xe+2を浮動小数点数と見なしているのに対し、これは2つの整数の加算にすぎないためです。

cppreference によると:

最大のムンクのため、eおよびEで終わる16進整数定数の後に、演算子+または-が続く場合、ソース内で空白または括弧を使用して演算子から区切る必要があります。

int x = 0xE+2;   // error
int y = 0xa+2;   // OK
int z = 0xE +2;  // OK
int q = (0xE)+2; // OK
20
msc