これが私が試したコードです。得られた出力を理解できませんでした。なぜここに10ではなくガベージ値が出力されるのですか?
#include<stdio.h>
#include<string.h>
void f(int);
void (*foo)(float)=f;
int main()
{
foo(10);
return 0;
}
void f(int i)
{
printf("%d\n",i);
}
出力はごみの値です。
これが私が試したコードです。得られた出力を理解できませんでした。なぜここに10ではなくガベージ値が出力されるのですか?
#include<stdio.h>
#include<string.h>
void f(int);
void (*foo)(float)=f;
int main()
{
foo(10);
return 0;
}
void f(int i)
{
printf("%d\n",i);
}
出力はごみの値です。
これは未定義の動作です。
fをとる関数である、を呼び出しています。これは、をとる関数intであるかのようにfloatです。実際の動作は、プラットフォームの呼び出し規約がどのように機能するかによって異なります。たとえば、intパラメータfloatが同じレジスタまたは同じスタックスロットで渡された場合、結果は、として表さ10.0fれる浮動小数点ビットパターンのエイリアスになりintます。一方int、float異なる方法で渡された場合、結果は適切なレジスタまたはスタックスロットにあったガベージになります。
また、これは未定義の動作であるため、コンパイラーは自由に好きなことを実行できます。オプティマイザーが有効になっている場合は、実行できる可能性があります。
この場合、コンパイラは通常、警告またはエラーを出力します。たとえば、GCC は次のように言います。
program.c:4: warning: initialization from incompatible pointer type
Intel C Compiler はより詳細です。
program.c(4): warning #144: a value of type "void (*)(int)" cannot be used to initialize an entity of type "void (*)(float)"
void (*foo)(float)=f;
^
引数値10を浮動小数点に変換する以外に、64 ビット マシンで実行している場合は別のオプションがあります - 呼び出し規則です。整数引数は通常の整数 CPU レジスタ ( 、 など) を介して渡されますがRDI、RSI浮動小数点引数は SSE レジスタ ( XMM0、XMM1など) で渡されます。何らかの形で浮動小数点値が整数値と互換性があったとしても、呼び出された関数はXMM0、値を参照するレジスタ ( ) とは異なるレジスタ ( ) で引数を受け取りRDIます。
32 ビット マシンでは、整数引数と浮動小数点引数の両方がスタックに渡されます。最終的に1092616192は10.0、IEEE 754 単精度浮動小数点表現だけです。
1092616192 = 0|10000010|01000000000000000000000
| | |
| | +---> significand = 1 + 2^(-2) = 1.25
| |
| +------------> exponent = 130 - 127 = +3
|
+--------------> sign = 0 (positive)
1.25 * 2^3 = 1.25 * 8 = 10.0
未定義の動作のため。
呼び出し throughfoo()は整数10をに変換しfloat、これは by として解釈さintれf()ます。
これが明確に定義されていないことは確かです。