これが私が試したコードです。得られた出力を理解できませんでした。なぜここに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()
ます。
これが明確に定義されていないことは確かです。