在 unsigned short 的情况下,我将 383 向左移动了 11 个位置,然后在同一条指令中再次将其向右移动了 15 个位置,我预计该值为 1 但它是 27。但是当我在不同的指令中同时使用这两个移位操作时之后(先左移,然后右移),输出为 1。这是一个示例代码:-
unsigned short seed = 383;
printf("size of short: %d\n",sizeof(short));
unsigned short seedout,seed1,seed2,seed3,seedout1;
seed1 = (seed<<11);
seed2 = (seed1>>15);
seed3 = ((seed<<11)>>15);
printf("seed1 :%d\t seed2: %d\t seed3: %d\n",seed1,seed2,seed3);
Run Code Online (Sandbox Code Playgroud)
它的输出是:
size of short: 2
seed1 :63488 seed2: 1 seed3: 23
seedout1: 8 seedout :382
Process returned 0 (0x0) execution time : 0.154 s
Run Code Online (Sandbox Code Playgroud)
关于左移、类型符号和隐式提升:
每当某些内容左移到有符号整数类型的符号位时,我们就会调用未定义的行为。同样,当左移负值时,我们也会调用未定义的行为。
因此,我们必须始终确保 的左操作数<<是无符号的。问题是,这是一个小整数类型,因此每当在表达式中使用时,它都会受到隐式类型提升的unsigned short影响。移位运算符总是整数提升左操作数:
C17 6.5.7:
对每个操作数执行整数提升。结果的类型是提升后的左操作数的类型。
(这使得移位成为一种特殊情况,因为它们不关心右操作数的类型,而只查看左操作数。)
unsigned short因此,在 16 位系统中,您将遇到提升为 的情况unsigned int,因为 16 位int无法保存 16 位 的所有值unsigned short。没关系,这不是危险的转换。
然而,在 32 位系统上,unsigned short将升级到int已签名的位置。如果你左移一个像0x8000(MSB) 设置 15 位或更多的值,你最终会将数据移入提升的符号位int,这是一个微妙且可能严重的错误。例如,在我的 Windows 计算机上打印“oops”:
#include <stdio.h>
int main (void)
{
unsigned short x=0x8000;
if((x<<16) < 0) ) // undefined behavior
puts("oops");
}
Run Code Online (Sandbox Code Playgroud)
但编译器也可以假设左移x永远不会产生值< x,并在优化时删除整个机器代码。
我们需要确保我们永远不会意外地得到签名类型!这意味着我们必须知道隐式类型提升在 C 中是如何工作的。
至于左移unsigned int或更大的无符号类型,只要我们的移位不超过(提升的)类型本身的宽度(在 32 位系统上超过 31 位),那就是完全明确定义的。移出的任何位都将被丢弃,如果右移,它将始终是逻辑移位,其中零从右侧移入。
回答实际问题:
您的unsigned short整数int在 32 位系统上提升为整数。这允许移动超出无符号短整型的 16 位,但是如果您通过将结果保存在 an 中来丢弃这些额外的位unsigned short,则最终会得到以下结果:
383 = 0x17F
0x17f << 11 = 0xBF800
0xBF800 truncated to 16 bits = 0xF800 = 63488
0xF800 >> 15 = 0x1
Run Code Online (Sandbox Code Playgroud)
但是,如果跳过中间步骤截断到 15 位,则会出现以下情况:
0xBF800 >> 15 = 0x17 = 23
Run Code Online (Sandbox Code Playgroud)
但同样,这只是运气,因为这次我们最终没有将数据移入符号位。
另一个例子,执行此代码时,您可能期望获得值 0 或值 32768:
unsigned short x=32768;
printf("%d", x<<16>>16);
Run Code Online (Sandbox Code Playgroud)
但它在我的 2 台补充 PC 上打印 -32768。调用x<<16未定义的行为,然后>>16明显的符号扩展了结果。
这些微妙的转变错误很常见,特别是在嵌入式系统中。数量惊人的所有 C 程序都是由不了解隐式提升的人编写的。
为了清楚起见,您比较
unsigned short seed1 = (seed<<11);
unsigned short seed2 = (seed1>>15);
Run Code Online (Sandbox Code Playgroud)
一方面和
unsigned short seed3 = ((seed<<11)>>15);
Run Code Online (Sandbox Code Playgroud)
另一方面。
第一个获取移位操作的结果,将其存储在一个unsigned short变量中(在您的平台上显然是 16 位),然后再次右移这个结果。
第二个立即改变结果。
这是不同的原因。左移的位保留如下:
虽然seed是unsigned short,seed<<11是signed int。因此,这些位不会像存储结果时那样被截断,而是保留在中间signed int. 只有赋值给seed1值unsigned short,这会导致位的裁剪。
换句话说:你的第二个例子仅仅相当于
int seed1 = (seed<<11); // instead of short
unsigned short seed2 = (seed1>>15);
Run Code Online (Sandbox Code Playgroud)
goo*_*ion -2
我将 383 向左移动 11 个位置,并在同一条指令中再次将其向右移动 15 个位置,我预计该值为 1,但结果是 27
简单的数学计算,你将它向右移动了 4 位,相当于除以 16。
将 383 除以 16,得到 27(当然是整数除法)。
请注意,“简单地将其移位 4 位”部分成立是因为:
unsigned操作数,这意味着你右移时没有“拖1”顺便说一句,关于上面的第二个项目符号 - 当您分部分执行此操作并将中间结果存储到 中时unsigned short,您确实会丢失数据并得到不同的结果。
换句话说,在执行 时seed<<11,编译器使用 32 位运算,而在将其存储到 时seed1,仅保留先前结果的 LSB 部分。
编辑:
上面的 27 应该是 23。我从你的描述中复制了这个,但没有检查,尽管我看到你确实在你的问题下面提到了 23,所以我假设 27 是一个简单的拼写错误......
| 归档时间: |
|
| 查看次数: |
1346 次 |
| 最近记录: |