69

int: %hhd%hd%ldおよび%lldmean signed charshort、に適用できる (少なくとも C99 では) 長さ修飾子がlongありlong longます。double: %Lfmeansに適用可能な長さ修飾子さえありlong doubleます。

問題は、なぜ省略したのfloatかということです。パターンに従って、それはだったかもしれません%hf

4

8 に答える 8

47

C の可変個引数関数呼び出しでは、任意のfloat引数が a に昇格 (つまり、変換) されるdoubleため、printfaを取得し、その実装内でそれを取得するためにdouble使用します。va_arg(arglist, double)

過去 (C89 および K&R C) では、すべてのfloat引数がdouble. 現在の標準では、明示的なプロトタイプを持つ固定アリティ関数のこの昇格を省略しています。これは、実装のABIおよび呼び出し規則に関連しています (詳細は説明されています) 。実際には、float値は引数として渡されたときに倍精度浮動小数点レジスタにロードされることがよくありますが、詳細はさまざまです。例としてLinux x86-64 ABI 仕様を読んでください。

また、特定のフォーマット制御文字列を指定する実際的な理由はありません。出力の幅を (たとえば で)必要に応じてfloat調整できるためです。%8.5f%hdscanfprintf

それ以上に、理由( -promoted to in caller-in の%hf指定を省略する)は歴史的なものだと思います: 最初は、C はシステム プログラミング言語であり、HPC 言語ではありませんでした (Fortran はおそらく 1990 年代後半まで HPC で好まれていました)。あまり重要ではありませんでした。それは、メモリ消費量を減らす方法のように考えられていました(そして今でもそうです) 。また、今日の FPU は (デスクトップまたはサーバー コンピューター上で) 十分に高速であるため、メモリ使用量を減らす手段としての except の使用を避けることができます。基本的に、 everyはどこかに (おそらくFPU または CPU の内部) に変換されていると考えるべきです。floatdoubleprintffloatshortfloatfloatdouble

実際には、あなたの質問は次のように言い換えることができます: なぜ%hd存在するのかprintf(基本的に役に立たない場所。渡したときにprintfが取得されますが、それが必要です!)。理由はわかりませんが、システムプログラミングよりも便利かもしれないと思います。intshortscanf

倍精度値が -s の範囲外である場合の未定義の動作で、 for に受け入れられるように次の ISO C 標準をロビー活動することに時間を費やすことができます ( at 呼び出しに昇格され、 -s が に昇格されます) 、for%hfによって対称的に受け入れられます。ポインター。頑張ってください。printffloatdoubleprintfshortintfloat%hfscanffloat

于 2015-09-11T07:31:18.753 に答える
15

デフォルトの引数の昇格のため。

printf()は可変引数関数 (...そのシグネチャ内) であり、すべてのfloat引数は に昇格されdoubleます。

C11 §6.5.2.2 関数呼び出し

6 呼び出された関数を示す式がプロトタイプを含まない型を持つ場合、各引数に対して整数昇格が実行され、型を持つ引数floatは に昇格されdoubleます。これらは、デフォルト引数プロモーションと呼ばれます。

7 関数プロトタイプ宣言子の省略記号表記により、最後に宣言されたパラメーターの後で引数の型変換が停止します。デフォルトの引数昇格は、末尾の引数に対して実行されます。

于 2015-09-11T07:32:24.107 に答える
10

可変引数関数を呼び出すときのデフォルトの引数昇格により、値は関数呼び出しの前にfloat暗黙的に に変換され、double値をに渡す方法はありませんfloatprintffloatに値を渡す方法がないため、値printfの明示的な書式指定子は必要ありませんfloat

そうは言っても、AntoineLはコメントで興味深い点を提起しました%lf(現在scanfは引数 type に対応するために使用されていますdouble *) はlong float、C89 以前の時代には型シノニムであった " "をかつて意味していた可能性があります。 C99の根拠。その論理により、が に変換された値を%f表すことを意図していたのは理にかなっているかもしれません。floatdouble


hh長さ修飾子とh長さ修飾子に関して、%hhuこれらの書式指定子の明確に定義された使用例を示します。たとえば、次のように、ラージまたはキャストなし%huで最下位バイトを出力できます。unsigned intunsigned short

printf("%hhu\n", UINT_MAX); // This will print (unsigned char)  UINT_MAX
printf("%hu\n",  UINT_MAX); // This will print (unsigned short) UINT_MAX

intからcharまたはへの縮小変換のshort結果は特に明確に定義されていませんが、少なくとも実装定義です。つまり、この決定を実際に文書化するには実装が必要です。

パターンに従って、それはそうあるべきでした%hf

観察したパターンに従って、%hfの範囲外の値を に変換する必要がありfloatますfloat。しかし、 から へのそのような狭義の変換は未定義の動作doubleをもたらし、.などはありません。あなたが見るパターンは意味がありません。float unsigned float


正式には、 は引数%lfを意味しません。引数long doubleを渡すと、未定義の動作が呼び出されます。ドキュメントから次のことが明示されています。long double

l(エル) ... に続く、、、、、、、、aまたは変換指定子にAe影響Eしません。fFgG

他の誰もこれに気付いていないことに驚いていますか?%lfdoubleと同じように引数を示します%f。を出力したい場合は、 (大文字のエル)long doubleを使用します。%Lf

%lffor bothprintfと andscanfに対応するdoubleanddouble *引数...%fが例外的であるのは、先に述べた理由により、デフォルトの引数の昇格のみが理由であることが、今後は理に​​かなっているはずです。

...%Ldという意味でもありませんlong。つまり、未定義の動作です。

于 2015-09-11T08:09:05.637 に答える
5

ISO C11 標準6.5.2.2 Function calls /6およびから/7、式のコンテキストでの関数呼び出しについて説明しています (私の強調):

6/ 呼び出された関数を示す式の型がプロトタイプを含まない場合、各引数に対して整数昇格が実行され、float 型の引数は double に昇格されます。これらは、デフォルト引数プロモーションと呼ばれます。

7/ 呼び出された関数を示す式がプロトタイプを含む型を持っている場合、引数は、あたかも代入によるかのように、対応するパラメーターの型に暗黙的に変換され、各パラメーターの型が非修飾バージョンになります。その宣言された型。関数プロトタイプ宣言子の省略記号表記により、最後に宣言されたパラメーターの後で引数の型変換が停止します。デフォルトの引数昇格は、末尾の引数に対して実行されます。

これは、プロトタイプの のfloat後の引数が に変換され、呼び出しのファミリーがそのように定義されることを意味します ( et seq):...doubleprintf7.21.6.11

int fprintf(FILE * restrict stream, const char * restrict format, ...);

したがって、printf()-family 呼び出しが実際にfloat を受け取る方法がないため、特別な書式指定子 (または修飾子) を使用する意味はほとんどありません。

于 2015-09-11T07:51:36.290 に答える
2

%hhd%hd%ldおよびは、既定の引数の昇格のために冗長ですが、フォーマット文字列を との一貫性を高めるために%lldに追加されました。printfscanfprintf

では、なぜ%hf追加されなかったのfloatですか? それは簡単です:scanfの動作を見ると、float既に書式指定子があります。です%f。のフォーマット指定子double%lf.

それ%lfはまさに C99 が に追加したものprintfです。C99 より前では、 の動作は%lf定義されていませんでした (標準の定義が省略されているため)。C99 の時点では、 のシノニムです%f

于 2015-09-12T10:33:39.587 に答える
2

float、double、または long double の個別の書式指定子があることを考えるとscanf、理由がわかりませんしprintf、同様の関数が同様の方法で実装されていませんでしたが、それが C / C++ と標準が最終的に終わった方法です。

プロセッサと現在のモードによっては、プッシュまたはポップ操作の最小サイズに問題が発生する可能性がありますが、これは、ローカル変数または構造内の変数のデフォルトの配置と同様に、デフォルトのパディングで処理できた可能性があります。Microsoft は、80 ビット (10 バイト) のサポートをlong double16 ビットから 32 / 64 ビット コンパイラに移行したときに廃止し、現在はs (64 ビット / 8 バイト)long doubleと同じように扱っています。double必要に応じて 12 または 16 バイトの境界までパディングすることもできましたが、これは行われませんでした。

于 2015-09-11T08:43:38.763 に答える
2

fscanf の下にある C の理論的根拠を読むと、次のことがわかります。

C99 の新機能: hh および ll の長さ修飾子が C99 で追加されました。ll は、新しい long long int 型をサポートします。hh は、文字型を他のすべての整数型と同じように扱う機能を追加します。これは、SCNd8 などのマクロを実装するのに役立ちます (7.18 を参照)。

したがってhh、すべての新しい型をサポートする目的で が追加されたと考えられstdint.hます。これは、小さな整数には長さ修飾子が追加されたが、小さな浮動小数点数には追加されなかった理由を説明できます。

C90 が矛盾して持っていた理由を説明していませんが、そうではありませhhh。C90 で指定されている言語は常に一貫しているわけではなく、単純です。そして、それ以降のバージョンは矛盾を継承しています。

于 2015-09-11T09:15:32.917 に答える
2

C が発明されたとき、すべての浮動小数点値はdouble、計算で使用される前、または関数 (を含む) に渡される前に、共通の型 (つまり ) に変換されていたため、浮動小数点型を区別するprintf必要はありませんでした。printf

double演算の効率と精度を向上させるために、IEEE-754 浮動小数点標準は、通常の 64 ビットよりも大きいが高速に処理できる80 ビット型を定義しました。a=b+c+d;のような式が与えられた場合、合計を計算するよりも、すべてを 80 ビット型に変換し、3 つの 80 ビット数値を加算し、結果を 64 ビット型に変換する方が高速かつ正確になるという意図がありました。(b+c)を 64 ビット型として追加し、それを に追加しdます。

新しい型をサポートするために、ANSI C はlong double実装が新しい 80 ビット型または 64 ビットを参照できる新しい型を定義しましたdouble。残念ながら、IEEE-754 の 80 ビット型の目的は、すべての値が昇格された方法で新しい型に自動的に昇格することでしたがdouble、ANSI は、新しい型が に渡されるように、printfまたは他の可変引数メソッドとは異なる方法でそれを作成しました。他の浮動小数点型であるため、このような自動昇格は受け入れられません。

したがって、C が作成されたときに存在した両方の浮動小数点型は同じ%f書式指定子を使用できますが、long double後で作成された には異なる%Lf書式指定子 (大文字 Lの) が必要です。

于 2015-09-11T21:12:09.350 に答える