我一直在阅读C Primer Plus书并得到了这个例子
#include <stdio.h>
int main(void)
{
float aboat = 32000.0;
double abet = 2.14e9;
long double dip = 5.32e-5;
printf("%f can be written %e\n", aboat, aboat);
printf("%f can be written %e\n", abet, abet);
printf("%f can be written %e\n", dip, dip);
return 0;
}
在我的macbook上运行之后,我对输出感到非常震惊:
32000.000000 can be written 3.200000e+04
2140000000.000000 can be written 2.140000e+09
2140000000.000000 can be written 2.140000e+09
所以我仔细观察并发现显示long double的正确格式是使用%Lf
。但是我仍然无法理解为什么我得到了双 abet
值,而不是我在 Cygwin , Ubuntu上运行它时得到的值和 iDeneb 大致是
-1950228512509697486020297654959439872418023994430148306244153100897726713609
013030397828640261329800797420159101801613476402327600937901161313172717568.0
00000 can be written 2.725000e+02
有什么想法吗?
答案 0 :(得分:7)
尝试查看OSX上的varargs调用约定,这可能会解释它。
我猜测编译器会在堆栈(或FPU寄存器)中传递第一个long double
参数,并在CPU寄存器(或堆栈)中传递第一个double
参数。无论哪种方式,他们都在不同的地方通过。因此,当进行第三次调用时,第二次调用的值仍然存在(并且被调用者将其拾取)。但这只是猜测。
答案 1 :(得分:4)
C Standard Library的printf()
函数是 variadic function 的示例,可以使用不同数量的参数。 C语言实现这一点的方式,被调用函数必须知道以哪种顺序传递的参数类型,以便它可以正确地解释它们。这就是您传递格式字符串的原因,以便printf()
可以正确地感知它必须打印的数据。
如果可变参数函数错误地解释了传递给它的参数,则C标准指定行为是 undefined ,即任何可能发生的事情(C89标准第4.8.1.2节)。在您的情况下,您将不匹配的格式和值传递给printf()
,这就是正在发生的事情。但是,如果你有一个不错的编译器并且你的警告级别变得合理,那么你应该在编译时得到警告。例如,在Cygwin上,我得到:
$ make go
cc -g -W -Wall -Wwrite-strings -ansi -pedantic go.c -o go
go.c: In function `main':
go.c:10: warning: double format, long double arg (arg 2)
go.c:10: warning: double format, long double arg (arg 3)
go.c:10: warning: double format, long double arg (arg 2)
go.c:10: warning: double format, long double arg (arg 3)
$
至于为什么你具体了解你所看到的,这将取决于具体的实施。在实践中,可能发生的是printf()
的特定实现是将long double
的前半部分解释为double
并打印与该特定位模式对应的值。但是,正如标准所述,它可以做任何它喜欢的事情。
答案 2 :(得分:4)
将%LF
的说明符用作long double
,而不是%lf
或%f
。 %LF
总是与%lf
具有不同的含义。
#include <stdio.h>
int main(void)
{
float aboat = 32000.0;
double abet = 2.14e9;
long double dip = 5.32e-5L;
printf("%f can be written %e\n", aboat, aboat);
printf("%f can be written %e\n", abet, abet);
printf("%LF can be written %LE\n", dip, dip);
return 0;
}
输出:
32000.000000 can be written 3.200000e+04
2140000000.000000 can be written 2.140000e+09
0.000053 can be written 5.320000E-05
答案 3 :(得分:3)
也许64位ABI的不同之处在于printf在与%LF参数完全不同的地方查找%f参数。
尝试查看程序集输出(gcc -S
)以查看是否为真。
答案 4 :(得分:1)
我正在阅读C Primer Plus,就像你我注意到同样的事情。看看我如何更改第三个printf语句的格式说明符。
#include <stdio.h>
#include <inttypes.h>
int main(void){
float aboat = 320000.0;
double abet = 2.214e9;
long double dip = 5.32e-5;
printf("%f can be written %e\n", aboat, aboat);
printf("%f can be written %e\n", abet, abet);
printf("%Lf can be written %Le\n", dip, dip);
return 0;
}
更改格式说明符后的结果
320000.000000 can be written 3.200000e+05
2214000000.000000 can be written 2.214000e+09
0.000053 can be written 5.320000e-05