在printf参数中提升类型是危险的吗?

时间:2012-10-21 07:16:12

标签: c logging printf

我的问题源于在尝试构建多个位深度平台(例如32/64)时尝试使用printf来记录事物。

一直在养成丑陋头脑的问题是尝试在多种体系结构上打印。在32位上它会像

printf(" my int: %d\n", myInt);

但是在64位上,它必须改为

print (" my int: %ld\n", (long)myInt);

我有两个相关的问题:

  1. 我的第一个想法是,当你告诉printf打印一个变量,给它一个格式时,它会查看该变量的地址并获取该格式所需的字节数。这一开始似乎是个大问题。例如,如果你有一个变量myChar,它是一个char(1个字节),但使用了%d的格式说明符,那将告诉printf转到myChar的地址并获取接下来的4个字节,将其视为int。如果是这种情况,似乎printf会从相邻变量中获取垃圾日期(因为它抓取了4个字节,但实际值只有1个字节)。然而,情况似乎并非如此。通过使用myChar并指定%d,printf抓取1个字节,然后用0表示填充高3个字节。我的理解在这里是否正确?

  2. 如果上述情况属实,那么始终将变量提升到最大值是否有任何真正的危害,以避免在32/64位情况下出现的问题类型。例如,如果你有一个短变量myShort和一个int变量myInt,那么打印它们总是有任何缺点:

    printf(“myShort%ld”,(long)myShort); printf(“myInt%ld”,(long)myInt);

  3. 感谢您的任何澄清。

4 个答案:

答案 0 :(得分:6)

关于printf:在您选择的情况下,“%d”必须按规范处理平台定义的“int”数据类型。无论是32位,64位还是128位线性AS / 400值都无关紧要。如果您想将值提升为更大的字段类型(并将该促销与相关格式字符串粒子匹配),您当然可以自由地这样做,

int a=0;
printf("%ld", (long)a);

当然是使用促销定义的行为。

我认为你问题的真正关键在于以下情况,以及强制晋升是否可以“解决”出现的任何问题。例如:

char ch = 'a';
printf("%d", ch);

或者说:

char ch = 'a';
printf("%ld", (long)ch);

或者也许这(这是你似乎试图避免的真实情况):

char ch = 'a';
printf("%ld", ch);

其中第一个将起作用,但这只是因为在va-arg列表中推送的任何堆栈的最小大小是int的平台大小。编译器会自动将值提升为int。由于“%d”期望平台int显示良好。

第二个将始终并且完全受支持。从charlong有明确且明确的促销。即使long为64位(或更大),它仍然有效。

第三个是UB。 printf正在寻找long,并且只会显示int的字节。如果这似乎在您的平台上“正常”,请检查intlong的平台宽度。它很可能“正常工作”,因为您的平台longint的位宽相同。当将代码移植到不适合的平台时,它会带来有趣的惊喜,并且由于它是通过va-arg推送的,所以在真实不同的宽度进入播放之前,您将看不到它。 / p>

所有这一切,现在抛出一个实际的地址到某些东西(任何东西,真的),例如scanf所要求的东西,我们正在寻找完全不同的东西。

int val;
sscanf("%ld",&val);

这是一个等待发生的段错误。如上所述,如果您的平台long和平台int的宽度相同,您将永远不会知道它。将此代码放在longint大小不同的框中,并为随后的核心文件的gdb加载做好准备。

答案 1 :(得分:1)

你说:

  

一直在养成丑陋头脑的问题是试图在多种体系结构上打印出来

尝试通过传入非该类型大小的值来尝试获取类型问题是危险的,是的。这就是编译器警告你的原因。似乎导致你出现问题的便携性概念并不是为了让printf感到高兴。

它旨在使您的程序运行,而不是在多个体系结构上崩溃。如果您有特定于平台的代码,则应使用#ifdef宏来解决它。

否则你正在掷骰子试图分层内存级别类型转换。

printf是一种便利而非类型转换方法。

看起来你专注于整体 - 你可能会侥幸逃脱。但总的来说,我不会依赖这样的技术。

答案 2 :(得分:0)

bools / _Boolscharsshorts首先转换为int(如果此转换保留了值,则保留为unsigned int )当传递给像printf()这样的可变函数时。同样,floats会转换为doubles

因此,如果您传递的内容小于intprintf()会抓住整个(unsigned) int而没有任何问题(除非传递的值实际上是unsigned int且如果您使用%d而不是%u进行打印,则会出现未定义的行为。)

其他类型,AFAIR,不会进行此类转换。

这一行:

print (" my int: %ld\n", (long)myInt);

不会在这条线上给你买任何东西:

printf(" my int: %d\n", myInt);

两者都有效,结果几乎相同。唯一的区别是前者可能导致更大的代码和更长的执行时间(如果sizeof(long) >= sizeof(int))。

答案 3 :(得分:0)

  1. 参数在堆栈中传递,每个条目具有固定宽度(32或64)位。编译器将整数,字符,短路“强制转换”为体系结构的本机宽度,或者在32体系结构的双(或长)情况下,它从堆栈中分配两个插槽。 “填充”用零完成,或者变量的符号位复制到剩余的位。 (称为符号位扩展名)

  2. 推广到64位的一个缺点是嵌入式系统缺乏兼容性,而这些系统通常不提供64位打印。此外,它在32位系统中意味着一些性能损失,因为前32位总是被传递和转换(有一个64位宽的除法涉及10)而没有任何实际用途。然而,更大的问题属于软件工程领域:“未来兼容”日志是否会给出错误的希望,即所有计算和系统的所有输入都在32位系统上以64位模式运行。

  3. (长)在32位架构中并不代表64位。这标志着(很久)。