关于 C 中的转换说明符

About conversion specifier in C

我对 C 中的转换说明符有疑问。

在第 5 句中,如果我使用 %lf%Lf 而不是 %f,则不会出现错误。但是为什么我用%f会出错呢?

#include <stdio.h>

int main(void)
{
    long double num;
    printf("value: ");
    scanf("%f",&num);  // If I use %lf or %Lf instead of %f, no error occurs. 
    printf("value: %f \n",num);
}

%f 用于读取 floats,而不是 doubles 或 long doubles。

%lf 用于阅读 doubles.

%Lf 用于阅读 long doubles.

如果您的程序在变量类型为long double时使用%lf,那只是巧合。它起作用可能是因为 sizeof(double) 与您平台上的 sizeof(long double) 相同。理论上是未定义的行为。

在 FreeBSD 系统(顺便说一句,POSIX)上查看 printf(3) 的手册页时,我得到以下信息:

The following length modifier is valid for the a, A, e, E, f, F, g, or G conversion:

     Modifier    a, A, e, E, f, F, g, G
     l (ell)     double (ignored, same behavior as without it)
     L           long double

我使用了 32 位浮点数据类型的转换。但这里的问题是,原因是因为不同的 float 格式具有不同的大小,并且 printf 函数需要知道它是哪一种,以便它可以正确地进行转换。在浮点数上使用 %Lf 可能会导致分段错误,因为转换正在访问变量外部的数据,因此您会得到未定义的行为。

  • 浮动:32 位
  • 双:64 位
  • 长双精度:80 位

现在 long double,实际大小由平台和实现定义。 80 位是 10 个字节,但这并不完全适合没有填充的 32 位对齐。因此大多数实现使用 96 位或 128 位(分别为 12 字节或 16 字节)来设置对齐方式。

这里要小心,仅仅因为它可能需要 128 位并不意味着它是 __float128(如果使用 gcc 或 clang)。至少有一个平台指定 long double 确实意味着 __float128(我认为是 SunOS),但它是在软件中实现的,速度很慢。此外,某些编译器(想到 Microsoft 和 Intel)long double = double 除非您在命令行上指定开关。