printfのHおよびHH修飾子の目的は何ですか?
-
14-10-2019 - |
質問
以外に %hn
と %hhn
(どこ h
また hh
のサイズを指定します 尖った オブジェクト)、のポイントは何ですか h
と hh
の修飾子 printf
フォーマット仕様?
標準がバリアード関数に適用する必要があるデフォルトのプロモーションにより、タイプの引数を渡すことは不可能です char
また short
(または署名された/符号なしのバリアント) printf
.
7.19.6.1(7)によると、 h
修飾子:
次のd、o、o、u、x、またはx変換の仕様が短いintまたは符号なしの短いint引数に適用されることを指定します(引数は整数プロモーションに従って宣伝されますが、その値は短いintに変換されますまたは印刷する前に署名されていない短いint);または、次のn変換特定が短いINT引数へのポインターに適用されること。
引数が実際にタイプの場合 short
また unsigned short
, 、次にプロモーション int
に戻る変換が続きます short
また unsigned short
同じものを生成します 価値 昇進として int
コンバージョンバックなし。したがって、タイプの引数の場合 short
また unsigned short
, %d
, %u
, 、などは同一の結果を与える必要があります %hd
, %hu
, 、など(そして同様に char
タイプと hh
).
私が知る限り、 h
また hh
修飾子が有用である可能性がある可能性があります。 int
の範囲外 short
また unsigned short
, 、例:
printf("%hu", 0x10000);
しかし、私の理解では、このように間違ったタイプを渡すと、とにかく未定義の動作が発生し、0が印刷されるとは思わなかったということです。
私が見た現実世界のケースの1つは、次のようなコードです。
char c = 0xf0;
printf("%hhx", c);
著者が印刷することを期待しているところ f0
実装にもかかわらず、平野があります char
署名されたタイプ(その場合、 printf("%x", c)
印刷します fffffff0
または類似)。しかし、この期待は保証されていますか?
(注:何が起こっているのかは、元のタイプが char
, 、昇進します int
に変換されました unsigned char
それ以外の char
, したがって、印刷される値を変更します。しかし、標準はこの動作を指定していますか、それとも壊れたソフトウェアが依存している可能性のある実装の詳細ですか?)
解決
考えられる理由の1つ:フォーマットされた入力関数でこれらの修飾子を使用する対称性については?私はそれが厳密に必要ではないことを知っていますが、多分そのために価値があったのでしょうか?
彼らは「H」および「HH」修飾子の対称性の重要性について言及していませんが C99 Rationaleドキュメント, 、委員会は、「%p」変換仕様がサポートされている理由についての考慮事項として言及しています。 fscanf()
(C99にとっては新しいものではありませんでしたが - 「%P」サポートはC90にあります):
%pを使用した入力ポインター変換はC89に追加されましたが、FPRINTFとの対称性については明らかに危険です。
セクションで fprintf()
, 、C99の根拠のドキュメントは、「HH」が追加されたことを議論していますが、単に読者をに参照します fscanf()
セクション:
%HHおよび%LLの長さ修飾子をC99に追加しました(§7.19.6.2を参照)。
私はそれが希薄なスレッドであることを知っていますが、とにかく推測しているので、私はそこにあるかもしれないものを何でも与えると思いました。
また、完全性のために、「H」修飾子は元のC89標準にありました - おそらく、修飾子を使用するための技術的要件がなかったとしても、既存の使用のために厳密に必要ではなかったとしても、おそらくそこにあるでしょう。
他のヒント
の %...x
モード、すべての値は符号なしと解釈されます。したがって、負の数は署名のない変換として印刷されます。ほとんどのプロセッサが使用する2の補数算術では、署名された負の数とその正の符号なしの等価物との間にビットパターンに違いはありません。これは、モジュラス算術によって定義されます(フィールドと1つの最大値を負の数に追加します。 C99標準に)。たくさんのソフトウェア - 特に使用する可能性が最も高いデバッグコード %x
- 署名された負の値とその符号なしのキャストのビット表現が同じであるというサイレントの仮定を立てます。これは2の補体マシンでのみ当てはまります。
このキャストのメカニズムは、価値の六角形の表現が、異なる整数表現が異なる範囲の場所のエッジ状態に達していない限り、2の補数に数がレンダリングされていることを常に意味することを意味します。これは、値0がすべての0のバイナリパターンで表されない算術表現にも当てはまります。
ネガ short
として表示されます unsigned long
したがって、ヘキシドシマルでは、どのマシンでも、 f
, 、プロモーションにおける暗黙の標識拡張のために printf
印刷します。 価値 同じですが、それはフィールドのサイズに関して本当に視覚的に誤解を招くものであり、単に存在しないかなりの量の範囲を意味します。
%hx
現実世界のユースケースから結論付けたとおり、このパディングを避けるために、表示された表現を切り捨てます。
の動作 printf
渡されたときに定義されていません int
の範囲外 short
それはaとして印刷する必要があります short
, 、しかし、最も簡単な実装は、生のダウンキャストによってハイビットをはるかに破棄するので、仕様はそうではありませんが 必要とする 特定の動作、ほとんどすべての正気の実装は、切り捨てを実行するだけです。しかし、一般的にそれを行うより良い方法があります。
printfがパディング値をパディングしたり、署名された値の署名のない表現を表示していない場合、 %h
あまり役に立ちません。
私が考えることができる唯一の用途は、 unsigned short
また unsigned char
とを使用します %x
変換仕様。単に裸を使用することはできません %x
- 値が宣伝される場合があります int
それよりも unsigned int
, 、そして未定義の動作があります。
あなたの代替案は、議論を明示的に投げかけることです unsigned
;または使用する %hx
/ %hhx
裸の議論で。
variadicの議論 printf()
et alはデフォルトの変換を使用して自動的に宣伝されるため、 short
また char
値は宣伝されます int
関数に渡されたとき。
の存在下で h
また hh
修飾子では、正しい動作を確実に取得するために、渡された値をマスクする必要があります。修飾子を使用すると、値をマスクする必要がなくなります。 printf()
実装はジョブを適切に行います。
具体的には、形式の場合 %hx
, 、内部のコード printf()
次のようなことができます:
va_list args;
va_start(args, format);
...
int i = va_arg(args, int);
unsigned short s = (unsigned short)i;
...print s correctly, as 4 hex digits maximum
...even on a machine with 64-bit `int`!
私はそれをただ仮定しています short
16ビット数量です。もちろん、標準は実際にそれを保証しません。
Unsigned Charをhexにフォーマットするときに、キャストを避けるのが便利だと思いました。
sprintf_s(tmpBuf, 3, "%2.2hhx", *(CEKey + i));
それはマイナーなコーディングの利便性であり、複数のキャスト(IMO)よりもきれいに見えます。
便利なもう1つの場所は、snprintfサイズのチェックです。 GCC7がSNPRINTFを使用するときにサイズチェックを追加して、これが失敗する
char arr[4];
char x='r';
snprintf(arr,sizeof(arr),"%d",r);
そのため
これは、%dを%hに変更したチャーアレイサイズを増やす代わりに、これらの修正を示すコミットです。これにより、より正確な説明も提供されます
私はそれが厳密に必要ではないことに同意します。したがって、その理由だけではCライブラリ機能では良くありません:)
異なるフラグの対称性には「いい」かもしれませんが、「変換」を隠すため、ほとんど逆効果です int
" ルール。