【问题标题】:Use the %ld conversion specifier to printf a double type in C使用 %ld 转换说明符在 C 中打印 double 类型
【发布时间】:2018-07-19 18:04:18
【问题描述】:

这里是部分源代码(示例代码来自《Cprimer 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 的前半部分,它被解释为 长整数。下一个 %ld 说明符再读取 4 个字节;这只是 n1 的后半部分并被解释为第二个长整数(参见 图 4.9)。同样,%ld 的第三个和第四个实例导致 n2 的前半部分和后半部分被读取并被解释为 还有两个长整数,所以虽然我们有正确的说明符 n3 和 n4 , printf() 正在读取错误的字节。

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

【问题讨论】:

  • 这是未定义的行为,简单明了。
  • 可能您在 x64 系统上,long 在整数寄存器中传递,double 在浮点寄存器中传递
  • 它高度依赖于您的特定编译器。您使用的是与本书不同的编译器,因此得到了不同的结果。
  • 这完全取决于平台,我得到了 2000000000 1234567890 4195840 140523256637664 here
  • 人们一直说这取决于您的平台,这是真的;然而这是explicitly undefined behavior:“如果任何参数不是相应转换规范的正确类型,则行为未定义。”任何事情都可能发生,包括根据月相在同一平台上的不同行为。

标签: c linux gcc


【解决方案1】:

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

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

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

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

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

【讨论】:

    【解决方案2】:

    来自标准 §7.21.6.6¶9

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

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

    使用正确的格式说明符(%f 用于浮点和双精度)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-26
      • 2021-07-13
      • 1970-01-01
      • 2013-08-14
      • 1970-01-01
      • 2018-02-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多