7

我的问题源于在尝试为多个位深度平台(例如 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);

感谢您的任何澄清。

4

4 回答 4

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一切都会好起来。

第二个将始终有效并得到完全支持。char从到有一个明确而明确的提升long。即使long是 64 位(或更大),它仍然可以工作。

三是一路UB。printf正在寻找 along并且将只显示 a 的字节int。如果这似乎在您的平台上“有效”,请检查您的平台宽度是否为intlong。它可能只是因为您的平台longint位宽相同而“工作”。当将代码移植到它们不存在的平台时,它会带来有趣的惊喜,并且由于它是通过 va-arg 推送的,因此在真正不同的宽度开始发挥作用之前,您不会看到它。

说了这么多,现在将实际地址扔给某些东西(实际上是任何东西),例如所需的scanf东西,我们正在寻找完全不同的东西。

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

这是一个等待发生的段错误。就像上面一样,如果您的平台long和平台int宽度相同,您将永远不会知道。将此代码放到一个大小不同的盒子中,longint为随后的核心文件的 gdb-load 做好准备。

于 2012-10-21T08:10:34.867 回答
1

你说:

一个不断抬头的问题是试图在多个架构上打印整数

通过传入不是该类型大小的值来尝试解决类型问题是否危险,是的。这就是编译器警告你的原因。似乎给您带来问题的可移植性概念并不是为了让 printf 高兴。

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

否则,您将掷骰子尝试对内存级别类型转换进行分层。

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

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

于 2012-10-21T08:05:46.913 回答
0

bools/ _Boolschars并且shorts在传递给int可变unsigned int参数函数(如printf(). 同样floats得到转换成doubles.

因此,如果您传递的内容小于int,将毫无问题地printf()抓取整体(除非传递的值实际上是 an并且您使用而不是打印它,您会得到未定义的行为)。(unsigned) intunsigned int%d%u

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

这一行:

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

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

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

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

于 2012-10-21T07:57:31.083 回答
0
  1. 参数在堆栈中传递,每个条目具有固定宽度(32 或 64)位。编译器将整数、字符、short 转换为体系结构的本机宽度,或者在 32 体系结构的 double(或 long long)的情况下,它从堆栈中分配两个插槽。“填充”要么用零完成,要么将变量的符号位复制到其余位。(称为符号位扩展)

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

(long) 在 32 位架构中并不意味着 64 位。这用 (long long) 表示。

于 2012-10-21T08:02:10.747 回答