使用%ld转换说明符在C中printf double类型

时间:2018-02-09 02:26:10

标签: c linux gcc

以下是源代码的一部分(“C primer plus”一书中的示例代码):

float n1 = 3.0;
double n2 = 3.0;
long n3 = 2000000000;
long n4 = 1234567890;
printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);

预期输出为:

0 1074266112 0 1074266112

该书明确解释了原因:

  

参数传递的机制取决于实现。这个   参数传递是如何在一个系统上工作的。函数调用看起来   如下:
printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);
这个电话   告诉计算机交出变量n1,n2的值,   n3和n4到电脑。这是一种常见的方式   实现。程序将值放在内存区域中   叫堆栈。当计算机将这些值放在堆栈上时   它由变量的类型引导,而不是由转换引导   符。因此,对于n1,它在堆栈上放置8个字节(   float被转换为double)。同样,它为其添加了8个字节   n2,然后是n3和n4各4个字节。然后控制转移到   printf()函数。此函数从堆栈中读取值   但是,当它这样做时,它会根据转换读取它们   符。 %ld说明符表示printf()应为4   printf()读取堆栈中的前4个字节作为第一个字节   值。这只是n1的前半部分,它被解释为a   长整数。下一个%ld说明符再读取4个字节;这只是   n1的后半部分被解释为第二个长整数(见   图4.9)。同样,%ld的第三个和第四个实例会导致   n2的第一和第二半被读取并被解释为   两个更长的整数,所以虽然我们有正确的说明符   n3和n4,printf()正在读取错误的字节。

我可以得到本书所说的内容,我自己的PC用于每种数据类型的内存与上面相同。
但是当我自己编译并运行代码时,我得到以下输出:
2000000000 1234567890 2147483626 0
我的开发环境是: Ubuntu16.04LTS,gcc 5.4.0,C11标准。我不知道是什么导致了我自己的输出和预期输出之间的区别。

2 个答案:

答案 0 :(得分:3)

  

这是一种常见的方式。程序将值放在称为堆栈的内存区域中。当计算机将这些值放在堆栈上时,它由变量的类型引导,而不是由转换说明符

引导

在大多数32位系统中都是如此,但在64位系统上,调用约定是不同的,如果可能,参数将在寄存器中传递。

请参阅this(System V AMD64 ABI与此相关)。

确保查看程序的程序集输出的一种方法。使用gcc,您可以使用-S输出程序集而不是二进制文件。

(请注意,我这里只讨论linux。根据您的操作系统,情况可能会有所不同,因为这是一个未定义的行为,您的编译器甚至您的编译器版本或标志)

答案 1 :(得分:3)

从标准§7.21.6.6¶9

  

如果转换规范无效,则行为未定义.282)如果任何参数不是相应转换规范的正确类型,则行为未定义。

这是你的问题。您可以阅读标准,您将看到标准永远不会说您必须使用堆栈或堆来实现内存访问或类似的东西。为了给出未定义行为的原因 - sizeof (long)(来自%ld格式说明符)正在读取每个案例并打印出来。但是存储double或其他变量所需的内存量可能与此不同。这将在大多数情况中打印原始变量的一部分。这是未定义的行为,这是正确的说法。

使用正确的格式说明符(%f表示float和double)。