【问题标题】:Understanding implicit conversions for printf了解 printf 的隐式转换
【发布时间】:2015-01-01 07:12:29
【问题描述】:

C99 标准区分隐式和显式类型转换(6.3 转换)。我猜想,但找不到,当目标类型比源类型更精确并且可以表示它的值时,会执行隐式转换。 [这就是我认为从 INT 到 DOUBLE 会发生的事情]。鉴于此,我看下面的例子:

#include <stdio.h>  // printf
#include <limits.h> // for INT_MIN
#include <stdint.h> // for endianess
#define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100)

int main()
{
  printf("sizeof(int): %lu\n", sizeof(int));
  printf("sizeof(float): %lu\n", sizeof(float));
  printf("sizeof(double): %lu\n", sizeof(double));
  printf( IS_BIG_ENDIAN == 1 ? "Big" : "Little" ); printf( " Endian\n" );

  int a = INT_MIN;
  printf("INT_MIN: %i\n", a);
  printf("INT_MIN as double (or float?): %e\n", a);
}

我很惊讶地发现该输出:

sizeof(int): 4
sizeof(float): 4
sizeof(double): 8
Little Endian
INT_MIN: -2147483648
INT_MIN as double (or float?): 6.916919e-323

所以打印的浮点值是一个接近最小次正规正双精度 4.9406564584124654 × 10^−324 的次正规浮点数。当我注释掉两个 printf 的字节序时,发生了奇怪的事情,我得到了另一个双精度值:

#include <stdio.h>  // printf
#include <limits.h> // for INT_MIN
#include <stdint.h> // for endianess
#define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100)

int main()
{
  printf("sizeof(int): %lu\n", sizeof(int));
  printf("sizeof(float): %lu\n", sizeof(float));
  printf("sizeof(double): %lu\n", sizeof(double));
  // printf( IS_BIG_ENDIAN == 1 ? "Big" : "Little" ); printf( " Endian\n" );

  int a = INT_MIN;
  printf("INT_MIN: %i\n", a);
  printf("INT_MIN as double (or float?): %e\n", a);
}

输出:

sizeof(int): 4
sizeof(float): 4
sizeof(double): 8
INT_MIN: -2147483648
INT_MIN as double (or float?): 4.940656e-324
  • gcc --version: (Ubuntu 4.8.2-19ubuntu1) 4.8.2
  • uname:x86_64 GNU/Linux
  • 编译器选项,其中:gcc -o x x.c -Wall -Wextra -std=c99 --pedantic
  • 是的,有一个警告:
x.c: In function ‘main’:
x.c:15:3: warning: format ‘%e’ expects argument of type ‘double’, but argument 2
          has type ‘int’ [-Wformat=]

   printf("INT_MIN as double (or float?): %e\n", a);
   ^

但我仍然无法理解到底发生了什么。

  • 在小字节序中,我认为 MIN_INT 为:00...0001 和 MIN_DBL(次正规)为 100..00#,从尾数开始,然后是指数,最后以 # 作为符号位。李>
  • 这种在 int 上应用“%e”格式说明符的形式是隐式转换吗?还是重新解释转换?

我迷路了,请赐教。

【问题讨论】:

  • cast-ing 与为printf() 提供不兼容的format specifiers 之间存在差异。后来的行动导致 UB。
  • 我建议你修改你的代码,有一个正确的printf和一个错误的printf,然后编译成汇编,看看两者的区别。您将了解如何在 varargs 函数中传递双精度数,以及它如何尝试将您的 int 作为双精度数传递。

标签: c gcc type-conversion implicit-conversion


【解决方案1】:
printf("INT_MIN as double (or float?): %e\n", a);

上面的行有问题您不能使用%e 来打印整数。行为未定义。

你应该使用

printf("INT_MIN as double (or float?): %e\n", (double)a);

double t = a;
printf("INT_MIN as double (or float?): %e\n", t);

Related post:这篇文章解释了在 printf 中使用不正确的打印说明符如何导致 UB。

【讨论】:

  • 相关帖子很有帮助。
  • @math 我很高兴它有帮助。
【解决方案2】:

va_arg 函数的参数没有被转换,语法上编译器对这些函数的参数一无所知,所以他不能这样做。但是,现代编译器确实知道解释格式字符串,因此当出现可疑情况时,它们能够警告您。当您看到来自 gcc 的警告时,就会发生这种情况。

更准确地说,对 narrow 整数类型进行了一些提升,它们被提升为int,而对于float,它们被提升为double。但这就是所有可能发生的魔法,在这里。

总之,始终使用正确的格式说明符。

顺便说一句,对于 size_tsizeof 表达式,正确的是 %zu

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-10
    • 1970-01-01
    • 2011-01-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多