在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);

谢谢你的任何澄清。

关于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推送的,所以在真正的不同宽度进入游戏之前你不会看到它。

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

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

这是一个等待发生的seg-fault。 就像上面一样,如果您的平台long和platform int的宽度相同,您将永远不会知道它。 将此代码带到一个框中,其中longint的大小不同,并为随后的核心文件的gdb加载做好准备。

你说:

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

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

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

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

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

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

_Bools / _Boolscharsshorts在传递给varivadic函数(如printf() )时首先转换为int (如果此转换保留值,则保留为unsigned int printf() 。 类似地, floats转换成doubles

因此,如果你传递小于int东西, printf()会抓住整个(unsigned) int而没有任何问题(除非传递的值实际上是unsigned int而你用%d而不是%u打印它,你得到未定义的行为)。

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

这一行:

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

是不是通过这条线购买任何东西:

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

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

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

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

(长)在32位架构中并不意味着64位。 这标志着(漫长的)。