このソースコードはCの文字列をオンに切り替えています。それはどのように行われますか?


106

私はいくつかのエミュレーターコードを読んでいて、本当に奇妙なことに対抗しました。

switch (reg){
    case 'eax':
    /* and so on*/
}

これはどのようにして可能ですか?switch整数型でしかできないと思いました。マクロの仕掛けはありますか?


29
文字列ではなく、 'eax'定数の整数値を列挙します
P__J__

12
二重引用符ではなく単一引用符。文字定数はに昇格されるためint、正当です。ただし、複数文字定数の値は実装定義であるため、コードは別のコンパイラーで期待どおりに機能しない可能性があります。例えば、eaxあるかもしれない0x650x6561780x656178000x7861650x6165、それ以外か何か。
Davislor 2017

2
@Davislor:変数「reg」の名前、およびeaxがx86レジスタであることを考えると、実装で定義された動作はOKであることが意図されていたと思います。コードで使用されているすべての場所で同じだからです。'eax' != 'ebx'もちろんと同じくらい長く、1つまたは2つの例でのみ失敗します。実際にはを想定*(int*)("eax") == 'eax'しているため、ほとんどの例で失敗するコードが存在する可能性があります。
スティーブジェソップ2017

2
@SteveJessop私はあなたの言うことに同意しませんが、同じアーキテクチャであっても、誰かが別のコンパイラでコードをコンパイルしようとして、異なる動作をする可能性があるという本当の危険があります。たとえば、'eax'と等しい'ebx'またはと比較'ax'し、switchステートメントが意図したとおりに機能しない場合があります。
Davislor 2017

1
regのデータ型を調べたり表示したりすると、そのすべての謎がすぐに解消されます。
2017

回答:


146

(「マクロトリック」の部分に答えることができるのはあなただけです。コードを貼り付けない限り。ただし、マクロを処理するための方法はここではあまりありません。正式にキーワードを再定義することはできません。その動作は定義されていません。)

プログラムの読みやすさを実現するために、機知に富んだ開発者は、実装で定義された動作を利用しています'eax'ない文字列が、複数文字定数。の前後の単一引用符に注意してくださいeax。ほとんどの場合、それはintあなたのケースであなたにその文字の組み合わせに固有のものを与えています。(かなり頻繁に各文字は32ビットで8ビットを占めますint)。そして、誰もがあなたができることswitchを知っていますint

最後に、標準的な参照:

C99標準は言う:

6.4.4.4p10:「複数の文字( 'ab'など)を含む、または1バイトの実行文字にマップされない文字またはエスケープシーケンスを含む整数文字定数の値は、実装定義です。 」


55
誰かがそれを見てパニックになった場合に備えて、「実装定義」が機能し、適切な方法でコンパイラによってドキュメント化される必要があります(標準では、動作が直感的であるか、ドキュメントが適切である必要はありませんが、 ...)。これは、「未定義」とは対照的に、彼らが書いているものを完全に理解しているコーダーに使用するのに「安全」です。
ロイセンコ2017

7
@ジャスティンそれは可能ですが、それはかなりひどいでしょう。答えが示唆することを実行しない場合は、可能性が最も高いです。次の可能性は、最初の文字を使用し、残りを無視することです。
Barmar

5
@ZanLynx私は肯定的ではありませんが、この機能はUnicodeおよび他のMBCS標準よりもずっと前から存在していると思います。メモリダンプのテキストのように見える「マジックナンバー」とRIFFスタイルのファイル形式チャンクIDは、私が最初に認識したアプリケーションでした。
Russell Borogove 2017

16
@ jpmc26これは未定義の動作ではなく、実装定義です。ですから、コンパイラのドキュメントが悪魔に言及していない限り、あなたの鼻は安全です。
Barmar 2017

7
@ZanLynx:元の意図は、Unicode、UTF-8、およびすべてのマルチバイト文字エンコーディングよりも20年近く古いと思われます。複数文字定数は、2バイト、3バイト、または4バイトのグループを表す整数を表現するための便利な方法でした(バイトとintのサイズによって異なります)。実装とアーキテクチャの不整合により、委員会はこれを実装定義として宣言しました。これは、'ab'from 'a'との値を計算する移植可能な方法がないことを意味し'b'ます。
chqrlie 2017

45

C標準に準拠(6.8.4.2 switchステートメント)

3 各ケースラベルの式は、整数定数式でなければなりません ...

および(6.6定数式)

6 整数定数式は整数型を持ち、整数定数、列挙定数、文字定数、結果が整数定数であるsizeof式、およびキャストの直接のオペランドである浮動定数であるオペランドのみを持つものとします 。整数定数式のキャスト演算子は、算術型を整数型に変換するだけです。ただし、オペランドの一部としてsizeof演算子に変換する場合を除きます。

今何'eax'ですか?

C標準(6.4.4.4文字定数)

2整数文字定数は「x」のように、単一引用符囲まれた1つ以上のマルチバイト文字のシーケンスです...

だから、'eax'整数文字定数は、同じセクションのパラグラフ10に応じです

  1. ...複数の文字を含む整数文字定数( 'ab'など)、または1バイトの実行文字にマップされない文字またはエスケープシーケンスを含む値は、実装によって定義されます。

したがって、最初に述べた引用によれば、ケースラベルとして使用できる整数定数式のオペランドになる可能性があります。

(一重引用符で囲まれた)文字定数にintは型があり、文字配列の型を持つ文字列リテラル(二重引用符で囲まれた文字のシーケンス)とは異なることに注意してください。


12

他の人が言ったように、これはint定数であり、その実際の値は実装定義です。

残りのコードは次のようになっていると思います

if (SOMETHING)
    reg='eax';
...
switch (reg){
    case 'eax':
    /* and so on*/
}

最初の部分の「eax」が2番目の部分の「eax」と同じ値であることを確認できるので、すべてうまくいきますよね?... 違う。

コメントで、@ Davislorは 'eax'のいくつかの可能な値をリストします。

... 0x650x6561780x656178000x7861650x6165、または何か他のもの

最初の潜在的な値に注意してください?それはただ'e'、他の2つの文字を無視します。問題は、プログラムは、おそらく使用している'eax''ebx'ように、と。これらすべての定数が'e'最終的に同じ値を持つ場合

switch (reg){
    case 'e':
       ...
    case 'e':
       ...
    ...
}

これは見栄えがしませんね。

「実装定義」の良い点は、プログラマーがコンパイラーのドキュメントをチェックして、これらの定数で賢明なことを行うかどうかを確認できることです。もしそうなら、家は無料です。

悪い点は、他の貧しい仲間がコードを取得し、他のコンパイラを使用してそれをコンパイルしようとする可能性があることです。インスタントコンパイルエラー。プログラムは移植可能ではありません。

@zwolがコメントで指摘したように、状況は思ったほど悪くはありません。悪い場合は、コードがコンパイルされません。これにより、少なくとも問題の正確なファイル名と行番号がわかります。それでも、動作するプログラムはありません。


1
何らかの形以外で assert('eax' != 'ebx'); //if this fails you can't compile the code because...は、元の作成者が構成を完全に置き換えることなく他のコンパイラの失敗を防ぐためにできることは何でもあります>
Dan Is Fiddling By Firelight

6
同じ値の2つのケースラベルは制約違反です(6.8.4.2p3:「...同じスイッチステートメント内の2つのケース定数式が変換後に同じ値を持つことはありません」)。したがって、すべてのコードがこれらの定数の値を不透明として扱います。これは、機能するか、コンパイルに失敗することが保証されています。
zwol 2017

悪い点は、別のコンパイラーでの貧しい仲間のコンパイルではおそらくコンパイル時エラーが発生しないことです(intの切り替えで問題ありません)。代わりに、実行時エラーが発生します...
tucuxi

1

コードフラグメントでは、マルチ文字文字定数と呼ばれる歴史的な奇妙さを使用します。これはマルチ文字とも呼ばれます。

'eax' 値が実装定義である整数定数です。

これは、マルチ文字とその使用方法に関する興味深いページですが、使用しないでください。

http://www.zipcon.net/~swhite/docs/computers/languages/c_multi-char_const.html


バックミラーを遠くに振り返ると、古き良き時代(https://www.bell-labs.com/usr/dmr/www/cman.pdf)のDennis RitchieによるオリジナルのCマニュアルで文字定数がどのように指定されたかがわかります。

2.3.2文字定数

文字定数は、単一引用符 '' '''で囲まれた1文字または2文字です。文字定数内では、単一引用符の前にバックスラッシュ '' \''を付ける必要があります。特定の非グラフィック文字と '' \''自体は、次の表に従ってエスケープできます:

    BS \b
    NL \n
    CR \r
    HT \t
    ddd \ddd
    \ \\

エスケープ '' \ddd''は、バックスラッシュとそれに続く1、2、または3桁の8進数で構成され、目的の文字の値を指定するために使用されます。この構造の特殊なケース\0は、ヌル文字を示す'' ''(数字が後に続かない)です。

文字定数は整数とまったく同じように動作します(特に、文字型のオブジェクトとは異なります)。PDP-11のアドレッシング構造に準拠して、長さ1の文字定数は、指定された文字のコードを下位バイトに、0を上位バイトに持っています。長さ2の文字定数には、下位バイトの最初の文字のコードと上位バイトの2番目の文字のコードがあります。複数の文字を含む文字定数は、本質的にマシンに依存するため、回避する必要があります。

最後のフレーズは、この奇妙な構造について覚えておく必要があるすべてです。複数の文字を含む文字定数は、本質的にマシンに依存するため、避ける必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.