C++ 中的移位运算符

发布于 2024-08-27 11:49:21 字数 182 浏览 6 评论 0原文

如果移位运算符后的值 大于位数 左边的操作数,结果是 不明确的。如果左侧操作数是 无符号,右移是逻辑 移位,以便填充高位 带零。如果左侧操作数 是有符号的,右移可能或可能 不是逻辑移位(即 行为未定义)。

有人可以解释一下上面几行的意思吗?

If the value after the shift operator
is greater than the number of bits in
the left-hand operand, the result is
undefined. If the left-hand operand is
unsigned, the right shift is a logical
shift so the upper bits will be filled
with zeros. If the left-hand operand
is signed, the right shift may or may
not be a logical shift (that is, the
behavior is undefined).

Can somebody explain me what the above lines mean??

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

苦妄 2024-09-03 11:49:21

这些行的含义并不重要,它们基本上是不正确的。

“如果移位运算符后的值
大于位数
左边的操作数,结果是
未定义。”

是正确的,但应该说“大于或等于”。5.8/1:

...如果以下情况,则行为未定义
右手操作数为负,或者
大于或等于长度
提升的左操作数的位。

未定义的行为意味着“不要这样做”(见下文)。也就是说,如果 int 在您的系统上是 32 位,那么您将无法有效地执行以下任何操作:

int a = 0; // this is OK
a >> 32;   // undefined behavior
a >> -1;   // UB
a << 32;   // UB
a = (0 << 32); // Either UB, or possibly an ill-formed program. I'm not sure.

“如果左侧操作数是无符号的,则右移是逻辑操作数移位,因此高位将用零填充。”

这是真的。 5.8/3 说:

如果 E1 具有无符号类型或如果 E1 有
有符号类型和非负值,
结果是的组成部分
E1 除以数量的商
2 的 E2 次方

如果这对您来说更有意义)。 >>1 与除以 2 相同,>>2 除以 4,>>3 除以 8 , 等等。在正值的二进制表示中,除以 2 与将所有位向右移动 1、丢弃最小位并用 0 填充最大位相同。

"如果左侧操作数是有符号,右移可能是也可能不是逻辑移位(即行为未定义)。”

第一部分是正确的(它可能是也可能不是逻辑移位 - 它在某些编译器/平台上但我认为到目前为止最常见的行为不是)。第二部分是假的,行为不是未定义的。未定义的行为意味着任何事情都可以发生 - 碰撞、恶魔从你的鼻子里飞出来、随机值等等。标准不在乎。在很多情况下,C++ 标准表示行为未定义,但这不是其中之一。

事实上,如果左侧操作数有符号且值为正,则其行为与无符号移位相同。

如果左侧操作数有符号且值为负,则结果值是实现定义的。不允许坠毁或着火。实现必须产生结果,并且实现的文档必须包含足够的信息来定义结果。实际上,“实现文档”从编译器文档开始,但这可能会隐式或显式地将您引向操作系统和/或 CPU 的其他文档。

再次来自标准 5.8/3:

如果E1有符号类型和负数
值,结果值为
实现定义的。

It doesn't matter too much what those lines mean, they are substantially incorrect.

"If the value after the shift operator
is greater than the number of bits in
the left-hand operand, the result is
undefined."

Is true, but should say "greater than or equal to". 5.8/1:

... the behavior is undefined if the
right hand operand is negative, or
greater than or equal to the length in
bits of the promoted left operand.

Undefined behavior means "don't do it" (see later). That is, if int is 32 bits on your system, then you can't validly do any of the following:

int a = 0; // this is OK
a >> 32;   // undefined behavior
a >> -1;   // UB
a << 32;   // UB
a = (0 << 32); // Either UB, or possibly an ill-formed program. I'm not sure.

"If the left-hand operand is unsigned, the right shift is a logical shift so the upper bits will be filled with zeros."

This is true. 5.8/3 says:

If E1 has unsigned type or if E1 has a
signed type and a nonnegative value,
the result is the integral part of the
quotient of E1 divided by the quantity
2 raised to the power E2

if that makes any more sense to you. >>1 is the same as dividing by 2, >>2 dividing by 4, >>3 by 8, and so on. In a binary representation of a positive value, dividing by 2 is the same as moving all the bits one to the right, discarding the smallest bit, and filling in the largest bit with 0.

"If the left-hand operand is signed, the right shift may or may not be a logical shift (that is, the behavior is undefined)."

First part is true (it may or may not be a logical shift - it is on some compilers/platforms but not others. I think by far the most common behaviour is that it is not). Second part is false, the behavior is not undefined. Undefined behavior means that anything is permitted to happen - a crash, demons flying out of your nose, a random value, whatever. The standard doesn't care. There are plenty of cases where the C++ standard says behavior is undefined, but this is not one of them.

In fact, if the left hand operand is signed, and the value is positive, then it behaves the same as an unsigned shift.

If the left hand operand is signed, and the value is negative, then the resulting value is implementation-defined. It isn't allowed to crash or catch fire. The implementation must produce a result, and the documentation for the implementation must contain enough information to define what the result will be. In practice, the "documentation for the implementation" starts with the compiler documentation, but that might refer you implicitly or explicitly to other docs for the OS and/or the CPU.

Again from the standard, 5.8/3:

If E1 has signed type and negative
value, the resulting value is
implementation-defined.

终止放荡 2024-09-03 11:49:21

我假设您知道转移意味着什么。假设您正在处理一个 8 位 char

unsigned char c;
c >> 9;
c >> 4;
signed char c;
c >> 4;

第一次转变,编译器可以自由地做任何它想做的事情,因为 9 > 。 8 [char 中的位数]。未定义的行为意味着所有的赌注都失败了,无法知道会发生什么。第二个转变是明确的。左侧有 0:11111111 变为 00001111。与第一个转变一样,第三个转变也是未定义的。

请注意,在第三种情况下,c 的值是什么并不重要。当它引用signed时,它表示变量的类型,而不是实际值是否大于零。 signed char c = 5signed char c = -5 都是有符号的,向右移位是未定义的行为。

I'm assuming you know what it means by shifting. Lets say you're dealing with a 8-bit chars

unsigned char c;
c >> 9;
c >> 4;
signed char c;
c >> 4;

The first shift, the compiler is free to do whatever it wants, because 9 > 8 [the number of bits in a char]. Undefined behavior means all bets are off, there is no way of knowing what will happen. The second shift is well defined. You get 0s on the left: 11111111 becomes 00001111. The third shift is, like the first, undefined.

Note that, in this third case, it doesn't matter what the value of c is. When it refers to signed, it means the type of the variable, not whether or not the actual value is greater than zero. signed char c = 5 and signed char c = -5 are both signed, and shifting to the right is undefined behavior.

痴意少年 2024-09-03 11:49:21

如果移位运算符后的值大于左侧操作数中的位数,则结果未定义。

这意味着 (unsigned int)x >>> 33 可以做任何事情 [1]

如果左侧操作数无符号,则右移是逻辑移位,因此高位将用零填充。

这意味着 0xFFFFFFFFu >>> 4 必须是 0x0FFFFFFu

如果左侧操作数有符号,则右移可能是也可能不是逻辑移位(即行为未定义)。

这意味着 0xFFFFFFFF >> 4 可以是0xFFFFFFFF(算术移位)或0x0FFFFFFF(逻辑移位)或物理定律允许的任何内容,即结果未定义。

[1]:在具有 32 位 int 的 32 位机器上。

If the value after the shift operator is greater than the number of bits in the left-hand operand, the result is undefined.

It means (unsigned int)x >> 33 can do anything[1].

If the left-hand operand is unsigned, the right shift is a logical shift so the upper bits will be filled with zeros.

It means 0xFFFFFFFFu >> 4 must be 0x0FFFFFFFu

If the left-hand operand is signed, the right shift may or may not be a logical shift (that is, the behavior is undefined).

It means 0xFFFFFFFF >> 4 can be 0xFFFFFFFF (arithmetic shift) or 0x0FFFFFFF (logical shift) or anything-allowed-by-physical-law, i.e. the result is undefined.

[1]: on 32-bit machine with a 32-bit int.

悸初 2024-09-03 11:49:21

如果移位运算符后的值
大于位数
左边的操作数,结果是
未定义。

如果尝试将 32 位整数移位 33,则结果是未定义的。即,它可能全为零,也可能不全零。

如果左侧操作数无符号,
右移是逻辑移位,所以
高位将被填充
零。

无符号数据类型右移时将用零填充。

所以1100>> 1==0110

如果左侧操作数有符号,
右移可能是也可能不是
逻辑转变(即行为
未定义)。

如果数据类型是有符号的,则行为未定义。有符号数据类型以特殊格式存储,其中最左边的位表示正或负。因此,对有符号整数进行移位可能不会达到您的预期。有关详细信息,请参阅维基百科文章。

http://en.wikipedia.org/wiki/Logical_shift

If the value after the shift operator
is greater than the number of bits in
the left-hand operand, the result is
undefined.

If you try to shift a 32-bit integer by 33 the result is undefined. i.e., It may or may not be all zeros.

If the left-hand operand is unsigned,
the right shift is a logical shift so
the upper bits will be filled with
zeros.

Unsigned data type will be padded with zeros when right shifting.

so 1100 >> 1 == 0110

If the left-hand operand is signed,
the right shift may or may not be a
logical shift (that is, the behavior
is undefined).

If the data type is signed, the behavior is not defined. Signed data types are stored in a special format, where the left most bit indicates positive or negative. So shifting on a signed integer may not do what you expect. See the Wikipedia article for details.

http://en.wikipedia.org/wiki/Logical_shift

无名指的心愿 2024-09-03 11:49:21

为了提供一些背景信息,这是该段落的开头:

移位运算符也可以操作位。左移运算符 (<<) 产生运算符左侧的操作数,向左移动运算符后面指定的位数。右移运算符 (>>) 将运算符左侧的操作数向右移动运算符后面指定的位数。

现在剩下的,有解释:

如果移位运算符后的值大于左侧操作数中的位数,则结果未定义。

如果您有一个 32 位整数,并且尝试位移 33 位,则这是不允许的,并且结果是未定义的。换句话说,结果可能是任何结果,否则你的程序可能会崩溃。

如果左侧操作数无符号,则右移是逻辑移位,因此高位将用零填充。

这表示它被定义为编写 a >> b 当 a 是无符号整数时。当右移时,最低有效位被删除,其他位向下移动,最高有效位变为零。

换句话说:

This:    110101000101010 >> 1
becomes: 011010100010101

如果左侧操作数有符号,则右移可能是也可能不是逻辑移位(即行为未定义)。

实际上,我相信这里的行为是当 a 为负数时定义的实现,以及当 a 为正数时定义的行为,而不是引用中建议的未定义。这意味着如果您执行 a >> ba 是负整数时,可能会发生许多不同的情况。要查看您获得的结果,您应该阅读编译器的文档。常见的实现是,如果数字为正,则移入 0;如果数字为负,则移入 1,但如果您希望编写可移植代码,则不应依赖此行为。

To give some context, here's the start of that paragraph:

The shift operators also manipulate bits. The left-shift operator (<<) produces the operand to the left of the operator shifted to the left by the number of bits specified after the operator. The right-shift operator (>>) produces the operand to the left of the operator shifted to the right by the number of bits specified after the operator.

Now the rest, with explanations:

If the value after the shift operator is greater than the number of bits in the left-hand operand, the result is undefined.

If you have a 32 bit integer and you try to bit shift 33 bits, that's not allowed and the result is undefined. In other words, the result could be anything, or your program could crash.

If the left-hand operand is unsigned, the right shift is a logical shift so the upper bits will be filled with zeros.

This says that it's defined to write a >> b when a is an unsigned int. As you shift right, the least significant bits are removed, other bits are shifted down, and the most significant bits become zero.

In other words:

This:    110101000101010 >> 1
becomes: 011010100010101

If the left-hand operand is signed, the right shift may or may not be a logical shift (that is, the behavior is undefined).

Actually I believe that the behaviour here is implementation defined when a is negative and defined when a is positive rather than undefined as suggested in the quote. This means that if you do a >> b when a is a negative integer, there are many different things that might happen. To see which you get, you should read the documentation for your compiler. A common implementation is to shift in zeros if the number is positive, and ones if the number is negative, but you shouldn't rely on this behaviour if you wish to write portable code.

深白境迁sunset 2024-09-03 11:49:21

我认为关键词是“未定义”,这意味着规范没有说明应该发生什么。大多数编译器在这种情况下会做一些明智的事情,但通常不能依赖于任何行为。通常最好避免调用未定义的行为,除非您使用的编译器的文档说明了它在特定情况下的作用。

第一句话表示,如果您尝试将 32 位值移位超过 32 位,则它是未定义的。

第二个表示,如果将无符号整数右移,左侧的位将被零填充。

第三个表示,如果将有符号 int 右移,则未定义将放入左手位的内容。

I suppose the key word is "undefined", which means that the specification does not say what should happen. Most compilers will do something sensible in such cases, but you cannot depend on any behaviour generally. It is usually best to avoid invoking undefined behavior unless the documentation for the compiler you are using states what it does in the specific case.

The first sentence says it's undefined if you try to shift, for example, a 32 bit value by more than 32 bits.

The second says that if you shift an unsigned int right, the left hand bits will get filled with zeros.

The third says that if you shift a signed int right, it is not defined what will be put in the left hand bits.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文