为什么printf需要强制转换?

时间:2011-11-28 23:06:38

标签: c printf

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

off_t a;
printf("%llu\n", (unsigned long long)a);
  • 为什么格式字符串不足
  • 如果没有铸造会有什么问题?

3 个答案:

答案 0 :(得分:14)

格式字符串不会告诉编译器对unsigned long long执行强制转换,它只是告诉printf它将收到unsigned long long。如果你传递的内容 unsigned long longoff_t可能不是),那么printf会误解它,结果会令人惊讶。

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


编辑添加:正如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;
}

原因是printf被声明为int printf(const char *format, ...),其中...是“可变参数”或“变量参数”符号,告诉编译器它可以接受format之后的任何数量和类型的参数。 (显然printf不能真正接受任何数量和类型的参数:它只能接受你告诉它的数字和类型,使用format。但编译器我对此一无所知;程序员需要处理它。)

即使使用...,编译器也会执行一些隐式转换,例如将char提升为int,将float提升为double。但这些转换并非特定于printf,并且它们不会也不会依赖于格式字符串。

答案 1 :(得分:4)

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

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

(例如,在linux上,32位机器上off_t的大小默认为32位,如果在包含相关系统头之前通过#define _FILE_OFFSET_BITS=64启用大文件支持,则为64位)

答案 2 :(得分:1)

printf的签名如下所示:

int printf(const char *format, ...);

vararg ...表示可以遵循任何内容,并且根据C的规则,只要包含格式字符串,就可以将任何内容传递给printf。 C根本没有任何构造来描述传递的对象类型的任何限制。这就是为什么你必须使用强化转换,以便传递的对象具有所需的类型。

这是C的典型特征,它在刚性和信任程序员之间划了界限。一个不相关的示例是您可以使用char *(没有const)来引用字符串文字,但如果您修改它们,您的程序可能会崩溃。