要打印多个类型off_t
,建议使用以下代码:
off_t a;
printf("%llu\n", (unsigned long long)a);
答案 0 :(得分:14)
格式字符串不会告诉编译器对unsigned long long
执行强制转换,它只是告诉printf
它将收到unsigned long long
。如果你传递的内容不 unsigned long long
(off_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
)来引用字符串文字,但如果您修改它们,您的程序可能会崩溃。