将 int 转换为 short 并截断后,如何确定新值?
When an int is cast to a short and truncated, how is the new value determined?
有人可以阐明在 C 中将整数转换为 short
时会发生什么吗?我正在使用 Raspberry Pi,所以我知道 int
是 32 位,因此 short
必须是 16 位。
假设我使用以下 C 代码为例:
int x = 0x1248642;
short sx = (short)x;
int y = sx;
我知道 x
会被截断,但有人能解释一下具体是怎么截断的吗?是否使用轮班?一个数字如何从 32 位截断为 16 位?
简单的把整数的高16位截掉。因此你的空头将变成 0x8642
这实际上是负数 -31166
.
32 位的值被截断为 16 位,就像将 32 厘米长的香蕉面包塞进 16 厘米长的平底锅中一样。一半可以装进去,仍然是香蕉面包,其余的将是 "gone".
sx
值将与 x
的 2 个最低有效字节相同,在这种情况下它将是 0x8642,它(如果解释为 16 位带符号整数)给出十进制的 -31166。
根据 ISO C 标准,当您将整数转换为有符号类型时,如果值超出目标类型的范围,则结果为 implementation-defined。 (或者可以引发 implementation-defined 信号,但我不知道有任何编译器会这样做。)
在实践中,最常见的行为是 high-order 位被丢弃。所以假设 int
是 32 位而 short
是 16 位,转换值 0x1248642
可能会产生看起来像 0x8642
的位模式。并假设有符号类型(几乎所有系统都使用)的 two's-complement 表示,high-order 位是符号位,因此结果的数值将为 -31166
.
int y = sx;
这也涉及隐式转换,从short
到int
。由于保证 int
的范围至少覆盖 short
的整个范围,因此该值不变。 (因为,在你的例子中,sx
的值恰好是负数,这种表示的变化很可能涉及 符号扩展 ,传播 1
符号位结果的所有 16 high-order 位。)
正如我所指出的,none 这些细节是语言标准所要求的。如果你真的想将值截断为更窄的类型,最好使用无符号类型(具有 language-specified 环绕行为)和可能的显式屏蔽操作,如下所示:
unsigned int x = 0x1248642;
unsigned short sx = x & 0xFFFF;
如果您有一个 32 位数量,您想要将其放入一个 16 位变量中,那么您应该做的第一件事就是决定如果该值不合适,您希望代码如何运行。一旦你决定了,你就可以弄清楚如何编写你想要的 C 代码。有时截断恰好是您想要的,在这种情况下您的任务将很容易,特别是如果您使用无符号类型。有时 out-of-range 值是一个错误,在这种情况下您需要检查它并决定如何处理错误。有时您可能希望值饱和而不是截断,因此您需要编写代码来做到这一点。
了解 C 中的转换如何工作很重要,但如果您从这个问题开始,您可能从错误的方向来解决问题。
截断发生在 CPU 寄存器中。它们有不同的大小:8/16/32/64 位。现在,您可以想象这样一个寄存器:
<--rax----------------------------------------------------------------> (64-bit)
<--eax----------------------------> (32-bit)
<--ax-----------> (16-bit)
<--ah--> <--al--> (8-bit high & low)
01100011 01100001 01110010 01110010 01111001 00100000 01101111 01101110
x
首先被赋予32位值0x1248642
。在记忆中*,它看起来像:
-----------------------------
| 01 | 24 | 86 | 42 |
-----------------------------
31..24 23..16 15..8 7..0
现在,编译器将 x
加载到寄存器中。从中,它可以简单地加载最低有效的 16 位(即 ax
)并将它们存储到 sx
.
*为简单起见,不考虑字节顺序
或许让代码自己说话:
#include <stdio.h>
#define BYTETOBINARYPATTERN "%d%d%d%d%d%d%d%d"
#define BYTETOBINARY(byte) \
((byte) & 0x80 ? 1 : 0), \
((byte) & 0x40 ? 1 : 0), \
((byte) & 0x20 ? 1 : 0), \
((byte) & 0x10 ? 1 : 0), \
((byte) & 0x08 ? 1 : 0), \
((byte) & 0x04 ? 1 : 0), \
((byte) & 0x02 ? 1 : 0), \
((byte) & 0x01 ? 1 : 0)
int main()
{
int x = 0x1248642;
short sx = (short) x;
int y = sx;
printf("%d\n", x);
printf("%hu\n", sx);
printf("%d\n", y);
printf("x: "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN"\n",
BYTETOBINARY(x>>24), BYTETOBINARY(x>>16), BYTETOBINARY(x>>8), BYTETOBINARY(x));
printf("sx: "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN"\n",
BYTETOBINARY(y>>8), BYTETOBINARY(y));
printf("y: "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN"\n",
BYTETOBINARY(y>>24), BYTETOBINARY(y>>16), BYTETOBINARY(y>>8), BYTETOBINARY(y));
return 0;
}
输出:
19170882
34370
-31166
x: 00000001 00100100 10000110 01000010
sx: 10000110 01000010
y: 11111111 11111111 10000110 01000010
如您所见,int
-> short
产生低 16 位,正如预期的那样。
将 short
转换为 int
会产生设置了 16 个高位的 short
。但是,我怀疑这是特定于实现和未定义的行为。您实质上是将 16 位内存解释为一个整数,它会读取 16 位额外的任何垃圾(如果编译器很好并且希望帮助您更快地找到错误,则为 1)。
我认为执行以下操作应该是安全的:
int y = 0x0000FFFF & sx;
显然你不会找回丢失的位,但这将保证高位被正确归零。
如果有人可以通过权威参考来验证 short -> int 高位行为,将不胜感激。
注:二进制宏改编自this answer。
有人可以阐明在 C 中将整数转换为 short
时会发生什么吗?我正在使用 Raspberry Pi,所以我知道 int
是 32 位,因此 short
必须是 16 位。
假设我使用以下 C 代码为例:
int x = 0x1248642;
short sx = (short)x;
int y = sx;
我知道 x
会被截断,但有人能解释一下具体是怎么截断的吗?是否使用轮班?一个数字如何从 32 位截断为 16 位?
简单的把整数的高16位截掉。因此你的空头将变成 0x8642
这实际上是负数 -31166
.
32 位的值被截断为 16 位,就像将 32 厘米长的香蕉面包塞进 16 厘米长的平底锅中一样。一半可以装进去,仍然是香蕉面包,其余的将是 "gone".
sx
值将与 x
的 2 个最低有效字节相同,在这种情况下它将是 0x8642,它(如果解释为 16 位带符号整数)给出十进制的 -31166。
根据 ISO C 标准,当您将整数转换为有符号类型时,如果值超出目标类型的范围,则结果为 implementation-defined。 (或者可以引发 implementation-defined 信号,但我不知道有任何编译器会这样做。)
在实践中,最常见的行为是 high-order 位被丢弃。所以假设 int
是 32 位而 short
是 16 位,转换值 0x1248642
可能会产生看起来像 0x8642
的位模式。并假设有符号类型(几乎所有系统都使用)的 two's-complement 表示,high-order 位是符号位,因此结果的数值将为 -31166
.
int y = sx;
这也涉及隐式转换,从short
到int
。由于保证 int
的范围至少覆盖 short
的整个范围,因此该值不变。 (因为,在你的例子中,sx
的值恰好是负数,这种表示的变化很可能涉及 符号扩展 ,传播 1
符号位结果的所有 16 high-order 位。)
正如我所指出的,none 这些细节是语言标准所要求的。如果你真的想将值截断为更窄的类型,最好使用无符号类型(具有 language-specified 环绕行为)和可能的显式屏蔽操作,如下所示:
unsigned int x = 0x1248642;
unsigned short sx = x & 0xFFFF;
如果您有一个 32 位数量,您想要将其放入一个 16 位变量中,那么您应该做的第一件事就是决定如果该值不合适,您希望代码如何运行。一旦你决定了,你就可以弄清楚如何编写你想要的 C 代码。有时截断恰好是您想要的,在这种情况下您的任务将很容易,特别是如果您使用无符号类型。有时 out-of-range 值是一个错误,在这种情况下您需要检查它并决定如何处理错误。有时您可能希望值饱和而不是截断,因此您需要编写代码来做到这一点。
了解 C 中的转换如何工作很重要,但如果您从这个问题开始,您可能从错误的方向来解决问题。
截断发生在 CPU 寄存器中。它们有不同的大小:8/16/32/64 位。现在,您可以想象这样一个寄存器:
<--rax----------------------------------------------------------------> (64-bit)
<--eax----------------------------> (32-bit)
<--ax-----------> (16-bit)
<--ah--> <--al--> (8-bit high & low)
01100011 01100001 01110010 01110010 01111001 00100000 01101111 01101110
x
首先被赋予32位值0x1248642
。在记忆中*,它看起来像:
-----------------------------
| 01 | 24 | 86 | 42 |
-----------------------------
31..24 23..16 15..8 7..0
现在,编译器将 x
加载到寄存器中。从中,它可以简单地加载最低有效的 16 位(即 ax
)并将它们存储到 sx
.
*为简单起见,不考虑字节顺序
或许让代码自己说话:
#include <stdio.h>
#define BYTETOBINARYPATTERN "%d%d%d%d%d%d%d%d"
#define BYTETOBINARY(byte) \
((byte) & 0x80 ? 1 : 0), \
((byte) & 0x40 ? 1 : 0), \
((byte) & 0x20 ? 1 : 0), \
((byte) & 0x10 ? 1 : 0), \
((byte) & 0x08 ? 1 : 0), \
((byte) & 0x04 ? 1 : 0), \
((byte) & 0x02 ? 1 : 0), \
((byte) & 0x01 ? 1 : 0)
int main()
{
int x = 0x1248642;
short sx = (short) x;
int y = sx;
printf("%d\n", x);
printf("%hu\n", sx);
printf("%d\n", y);
printf("x: "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN"\n",
BYTETOBINARY(x>>24), BYTETOBINARY(x>>16), BYTETOBINARY(x>>8), BYTETOBINARY(x));
printf("sx: "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN"\n",
BYTETOBINARY(y>>8), BYTETOBINARY(y));
printf("y: "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN"\n",
BYTETOBINARY(y>>24), BYTETOBINARY(y>>16), BYTETOBINARY(y>>8), BYTETOBINARY(y));
return 0;
}
输出:
19170882
34370
-31166
x: 00000001 00100100 10000110 01000010
sx: 10000110 01000010
y: 11111111 11111111 10000110 01000010
如您所见,int
-> short
产生低 16 位,正如预期的那样。
将 short
转换为 int
会产生设置了 16 个高位的 short
。但是,我怀疑这是特定于实现和未定义的行为。您实质上是将 16 位内存解释为一个整数,它会读取 16 位额外的任何垃圾(如果编译器很好并且希望帮助您更快地找到错误,则为 1)。
我认为执行以下操作应该是安全的:
int y = 0x0000FFFF & sx;
显然你不会找回丢失的位,但这将保证高位被正确归零。
如果有人可以通过权威参考来验证 short -> int 高位行为,将不胜感激。
注:二进制宏改编自this answer。