最初に、他のいくつかの回答で触れましたが、私の頭では十分に明確に述べていません。ライブラリ関数がor 引数をとるほとんどのコンテキストで整数を提供するのに役立ちます。コンパイラは自動的に変換を挿入します。たとえば、は明確に定義されており、とまったく同じように動作し、そこで使用される他の整数型式についても同様です。double
float
sqrt(0)
sqrt((double)0)
printf
違います。引数の数が可変であるため異なります。その関数プロトタイプは
extern int printf(const char *fmt, ...);
したがって、あなたが書くとき
printf(message, 0);
コンパイラーは、2番目の引数がどのタイプであるとprintf
想定するかについての情報を持ちません。引数式のタイプ、つまりのみint
です。したがって、ほとんどのライブラリ関数とは異なり、引数リストがフォーマット文字列の期待と一致することを確認するのはプログラマーの責任です。
(最新のコンパイラーは、フォーマット文字列を調べて、型の不一致があることを通知できますが、意図したとおりに変換を挿入し始めることはありません。 、数年後、あまり役に立たないコンパイラで再構築されたときよりも)
(int)0と(float)0.0がほとんどの最近のシステムでは、両方とも32ビットとして表され、そのすべてがゼロであるとすると、なぜそれが偶然に動作しないのでしょうか。C規格では「これは機能する必要はなく、ご自身で行う」と述べていますが、機能しない最も一般的な2つの理由を説明します。これはおそらく、それが必要でない理由を理解するのに役立つでしょう。
まず、歴史的な理由から、float
可変引数リストにを渡すと、に昇格されますdouble
。これは、ほとんどの最新のシステムでは64ビット幅です。したがってprintf("%f", 0)
、64を期待している呼び出し先に32のゼロビットのみを渡します。
同様に重要な2番目の理由は、浮動小数点関数の引数が整数の引数とは異なる場所に渡される可能性があることです。たとえば、ほとんどのCPUには整数と浮動小数点の値用に別々のレジスタファイルがあるため、引数0から4が整数の場合はレジスタr0からr4に、浮動小数点の場合は引数f0からf4に入るという規則になる場合があります。だから、printf("%f", 0)
そのゼロのためのレジスタF1に見えますが、それはすべてではありません。
printf
はを期待していdouble
ますが、あなたはそれにを与えていint
ます。float
そしてint
、あなたのマシン上で同じ大きさかもしれないが、0.0f
実際に変換され、double
可変長引数リスト(とに押し込まれたときprintf
を期待)。要するに、あなたはprintf
あなたが使用している指定子とあなたが提供している引数に基づいて交渉の終わりを達成していません。