为什么printf需要强制转换?

Beg*_*ner 13 c printf

要打印多种类型off_t,建议使用以下代码:

off_t a;
printf("%llu\n", (unsigned long long)a);
Run Code Online (Sandbox Code Playgroud)
  • 为什么格式字符串是不是就够了吗?
  • 如果没有铸造会有什么问题?

rua*_*akh 16

格式字符串不告诉编译器执行强制转换unsigned long long,它只是告诉printf它将接收到unsigned long long.如果你的东西的传递不是一个unsigned long long(这off_t可能不是),然后printf将简单地曲解它,以令人惊讶的结果.

原因是编译器不必知道有关格式字符串的任何信息.如果你写的话printf("%d", 3.0),一个好的编译器会给你一个警告信息,但是如果你编写的话,编译器可以做什么printf(s, 3.0),并且s是在运行时动态确定的字符串?


编辑添加:正如Keith Thompson在下面的评论中指出的那样,编译器可以在很多地方执行这种隐式转换.printf是一个相当特殊的例子,在一个案例中它不能.但是如果你声明一个函数接受一个unsigned long long,那么编译器执行转换:

#include <stdio.h>
#include <sys/types.h>


int print_llu(unsigned long long ull)
{
  return printf("%llu\n", ull); // O.K.; already converted
}


int main()
{
  off_t a;

  printf("%llu\n", a); // WRONG! Undefined behavior!
  printf("%llu\n", (unsigned long long) a); // O.K.; explicit conversion
  print_llu((unsigned long long) a); // O.K.; explicit conversion
  print_llu(a); // O.K.; implicit conversion

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

这样做的原因printf是声明为int printf(const char *format, ...),其中...是"可变参数"或"变量参数"符号,告诉编译器它可以接受任何数量和类型的参数format.(显然printf不能真正接受任何数量和类型的参数:它只能接受你告诉它使用的数字和类型format.但是编译器对此没有任何了解;它留给程序员来处理它. )

即使有...,编译器确实做了一些隐式转换,如推广charintfloatdouble.但是这些转换并不是特定的printf,并且它们不依赖于格式字符串,也不能依赖格式字符串.


nos*_*nos 5

问题是您不知道off_t有多大。它可以是64位类型或32位类型(或其他类型)。如果使用%llu,并且不传递(无符号)long long类型,则将得到未定义的行为,实际上,它可能只是打印垃圾。

不知道它有多大,简单的方法是将其转换为系统支持的最大合理类型,例如unsigned long long。这样使用%llu是安全的,因为由于强制转换,printf将收到一个无符号的long long类型。

(例如,在Linux上,默认情况下,在32位计算机上,off_t的大小为32位;如果#define _FILE_OFFSET_BITS=64在包含相关系统标头之前通过启用了大文件支持,则off_t的大小为64位)