C printf为什么以及如何选择匹配的参数?

C printf为什么以及如何选择匹配的参数?,c,C,有人问我,为什么这个代码会产生一个随机数: double a = 75.0; printf("%d\n", a); 我认为原因是,double的4个字节被解释为一个整数,但每次运行程序时打印的值都不同。所以我开始尝试更多的东西,发现: printf("%d\n", 75.0, 6); 实际上是打印出6号。因此,我认为编译器正在尝试修复参数,使其与格式字符串匹配,但后来我尝试了以下方法: const char *formats[] = { "%d %.1f\n", "%.1f %d\n" };

有人问我,为什么这个代码会产生一个随机数:

double a = 75.0;
printf("%d\n", a);
我认为原因是,
double
的4个字节被解释为一个整数,但每次运行程序时打印的值都不同。所以我开始尝试更多的东西,发现:

printf("%d\n", 75.0, 6);
实际上是打印出6号。因此,我认为编译器正在尝试修复参数,使其与格式字符串匹配,但后来我尝试了以下方法:

const char *formats[] = { "%d %.1f\n", "%.1f %d\n" };
int whichFormat = 0;
scanf("%d", &whichFormat);
printf(formats[whichFormat&1], 2.5, 7, 1.2);
格式字符串现在在编译时甚至不知道,但它仍然设法将参数类型与格式字符串匹配,根据输入打印
7 2.5
2.5 7
。未打印最后一个值(1.2)

所有这些都可以在声称使用GNU GCC 4.8.1时复制


这里发生了什么?

正在发生未定义的行为

这种行为是未定义的,因此很难推理,而且做实验也有点毫无意义,因为无法保证对于相同的输入,行为保持不变(即定义良好)。毕竟,这种行为是未定义的


例如,第一个示例可能从一个寄存器中读取预期的整数参数,而实际的浮点参数位于另一个寄存器中。我并不是说任何已知机器上都会发生这种情况,但它可能是这样。

未定义的行为正在发生

这种行为是未定义的,因此很难推理,而且做实验也有点毫无意义,因为无法保证对于相同的输入,行为保持不变(即定义良好)。毕竟,这种行为是未定义的


例如,第一个示例可能从一个寄存器中读取预期的整数参数,而实际的浮点参数位于另一个寄存器中。我不是说在任何已知的机器上都会发生这种情况,但它可能是这样的。

检查您平台的ABI可能指定整数和浮点参数在不同的寄存器组中传递,这会导致它看起来能够匹配正确的类型。编译器不太可能尝试“修复”参数以匹配转换说明符;更可能的是,此结果只是函数参数如何在特定体系结构上传递的工件(通过寄存器而不是堆栈)。检查您平台的ABI可能指定整数和浮点参数在不同的寄存器集中传递,这导致它看起来能够匹配正确的类型。编译器尝试“修复”的可能性非常小“与转换说明符匹配的参数;更可能的是,这个结果只是函数参数如何在这个特定体系结构上传递的一个工件(通过寄存器而不是堆栈)。检查x86-64上GCC的汇编程序输出表明您是对的。
double
参数放在XMM寄存器中。尽管了解恶意结果来自何处会很有教育意义,但纠正格式规范并继续前进要好得多。在X86_64上,肯定会发生类似的事情。如果它们是整数或指针,前四个函数参数将在寄存器中传递。在这里,第二个参数将是
printf
%esi
寄存器中找到的参数,我认为它与存储
函数参数的位置完全无关。检查x86-64上的GCC汇编程序输出表明您是对的。
double
参数放在XMM寄存器中。尽管了解恶意结果来自何处会很有教育意义,但纠正格式规范并继续前进要好得多。在X86_64上,肯定会发生类似的事情。如果它们是整数或指针,前四个函数参数将在寄存器中传递。这里,第二个参数就是
printf
%esi
寄存器中找到的参数,我认为,它与存储
函数参数的位置完全无关。