printf のように動作する error という関数を作成しようとしていますが、%s、%c、および %d 指定子のみをサポートしています。しかし、% の後に正当な文字が続かない場合の printf の動作を理解できませんでした。また、c99 標準マニュアルをループアップしようとしましたが、何も見つかりませんでした。誰か助けてくれませんか?
3 に答える
C99 の §7.19.6.1/9 (for fprintf
) には次のようなものがあります。
変換指定が無効な場合、動作は未定義です。
そのため、不正な形式の文字列は未定義の動作につながります。
フォーマット文字列の形式は正しいが、引数が実際には型と一致しない場合も同様です。
対応する変換仕様に対して正しい型でない引数がある場合、動作は未定義です。
あなたは、ベスト プラクティスとして何をすべきかよりも、実際に何が起こっているかに関心があるように見えるので、私はその点について言及し、章と節の引用は他の人に任せます。:-)
printf
フォーマット文字列は、処理するバイト数とそれらをフォーマットする方法を伝える「ヒント」です。間違った形式を指定すると、実際の型に関係なく、指定したとおりに引数を喜んで処理します。
これらのバイトの値に応じて、意味がある場合もあれば、意味不明な場合もあります。
簡単な (実装に依存する) 例を次に示します。
#include <stdio.h>
void main(int argc, char *argv[]) {
int x = 10; // LF
x <<= 8;
x += 13; // CR
x <<= 8;
x += 73; // I
x <<= 8;
x += 72; // H
printf("x = %d (%08x)\n", x, x);
printf("%.4s", &x);
}
これは、バイトに72`x
が含まれる (私のシステムでは 4 バイトの) 整数として定義されます。10
13
73
これらのバイトはたまたま、改行、キャリッジ リターン、I、および H の ASCII コードです。
1 つ目printf
は の 10 進値と 16 進値を表示しx
ます。自分で計算すると、期待どおりになることがわかります。
しかし、2 番目は 4 文字の文字列であるかのようにprintf
扱わx
れます。私のマシンはリトル エンディアンであるため、バイト オーダーが逆になり、ASCII 72、73、13、10 に対応する文字が出力されます。
H
I
CR
LF
もちろん、マットが指摘したように、これは の背後printf
にある仕組みを理解するのに役立ちますが、この動作に依存するコードを書くべきではありません。少なくとも、移植性はありません (たとえば、ビッグ エンディアンのマシンはバイト オーダーを変更しません)。
printf
のコーディング方法によって異なります。文字列内で%
が検出されると、関数は表示されると予想される文字の 1 つを見つけます。一致する場合は、一致したブロックを処理します。たとえば、にを渡すと、引数が純粋にスタックを介して渡さfloat
れる%d
場合に、整数を渡した変数が処理されます。同様に、 を渡すと、整数ビットがシステムの特定の浮動小数点表現として解釈されますint
。%f
また、R..がコメントで指摘したように、引数がレジスタを介して渡される場合、出力は異なります。渡された値が格納されるレジスターのタイプ (FPU レジスターまたは汎用レジスターなど)。特定のフォーマット指定子は、最初に遭遇したレジスターの最初の引数を取得します。
文字が一致しないものである場合、k
単に%k
文字列として出力されるか、関数が実装するものは何でも実行されます。
編集
この件については、C99 標準仕様に関する Mat の回答をご覧ください。