C++中位运算符的定义是什么?

3
根据标准规定,如果第一个操作数为负的有符号数,则运算符<<的行为未定义。 C++11 5.8.2
The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-
filled. If E1 has an unsigned type, the value of the result is E1 × 2 pow E2,
reduced modulo one more than the maximum value representable in the result type.
Otherwise, if E1 has a signed type and non-negative value, and E1 × 2 pow E2 is
representable in the result type, then that is the resulting value; otherwise,
the behavior is undefined

这是可以理解的,因为内存中整数的布局是由实现定义的。 C++11 3.9.1.7
this International Standard permits 2’s complement, 1’s complement and
signed magnitude representations for integral types.

另一方面,标准似乎并没有明确定义按位 & | 和 ^ 应该执行什么操作。

C++11 5.11 按位与运算符

and-expression:
    equality-expression
    and-expression & equality-expression
1 The usual arithmetic conversions are performed; the result is the bitwise 
AND function of the operands. The operator applies only to integral
or unscoped enumeration operands.

C++11 5.12 按位异或运算符

exclusive-or-expression:
    and-expression
    exclusive-or-expression ˆ and-expression
1 The usual arithmetic conversions are performed; the result is the bitwise
exclusive OR function of the operands. The operator applies only to integral
or unscoped enumeration operands.

C++11 5.13 按位或运算符

inclusive-or-expression:
    exclusive-or-expression
    inclusive-or-expression | exclusive-or-expression
1 The usual arithmetic conversions are performed; the result is the bitwise
inclusive OR function of its operands. The operator applies only to integral
or unscoped enumeration operands.

这些运算符的定义完全使我懵逼了。它们是否在标准中有说明?对于有符号整数,结果是实现定义的吗?

举个例子,让我们看看这段代码:

signed char a=-1;
signed char b=3;
signed char c=a&b;

使用二进制补码,a为1111 1111,b为0000 0011。最终c等于0000 0011(+3)。

使用一进制补码,a为1111 1110,b为0000 0011。那么c是否等于0000 0010(+2)?

使用符号-数量表示法,a为1000 0001,b为0000 0011。那么c是否等于0000 0001(+1)?

如果您可以访问使用一进制补码或符号-数量表示法的平台,那么这些平台上的结果是什么?


6
我确定按位布尔运算符只关心按位操作,不会涉及有符号性。 - Tony The Lion
@ Tony:那么为什么我们在 << 操作符中有一个 UB?有符号整数的位可以像无符号整数一样移动。 - Arnaud
@Arnaud:你是说它应该明确定义“按位运算”和“AND函数”这两个常识吗?还是你认为定义中还有其他缺失的内容? - Mike Seymour
@ Mike Seymour:我的问题是结果是实现定义的,这就是我觉得标准缺失的地方。其结果是,在编写可移植代码时,应避免使用带符号类型的这些运算符。 - Arnaud
关于位运算,我认为相反的观点更有意义:将一个位模式解释为整数是实现相关的,而对位模式进行位运算是由标准定义明确的。 - molbdnilo
显示剩余4条评论
4个回答

5

按位运算在每个位上独立地操作,无论每个位被解释为数值类型的一部分时意味着什么。

因此,是的,10000001 & 00000011 == 00000001,无论每个位是表示符号还是值的一部分。


如果操作数是有符号的,并且架构允许陷阱表示,那么会发生什么?你会得到UB吗? - Kevin

0
按位运算符 & | ^ 只对两个操作数的每一位执行其指定的操作,具体取决于底层类型的表示方法。
但是,移位操作有所不同。
例如,考虑一个字节的二进制补码 -1 = 11111111。然后将其向右移动一个位置。现在你的数字是 127(改变符号),还是-1(将1移入最高位而不是0)。(如果它是符号-大小表示,则情况也是如此。为了避免所有这些问题,标准简单地禁止了这种做法。

右移操作的实现是有定义的,并且在5.8.3中已经明确说明。但是,在位运算符中没有提到这一点。 - Arnaud

0

左移和右移对于负有符号整数的处理方式是按照标准来进行的,以便让实现可以使用“算术移位”机器指令。算术右移复制符号位,而逻辑右移则在左侧插入0。如果算术左移从左边缘移出的位与符号位不同,则可能在某些体系结构上产生溢出异常。因此,右移是实现定义的(因为结果始终有效但可能因实现而异),而左移是未定义的(因为结果可能是中断)。

按位逻辑运算符产生的位模式是完全指定的,但在有符号整数的情况下,结果可能是陷阱值(例如,在1的补码或符号-幅度体系结构中,-0不是有效值)。在这种情况下,根据第5节介绍的第4段,结果是未定义行为:

如果在表达式的求值过程中,结果在数学上没有定义或不在其类型的可表示值范围内,则行为是未定义的。


在一些机器上,即使使用二进制补码算法,-32768 也可能成为陷阱值。(-1 ^ 32767) 有没有保证是被明确定义的呢? - supercat
@supercat: (-1 ^ 32767) 不是良好定义的,因为 -1 的位表示是实现定义的。更直接地回答您的问题,按位操作可能会产生陷阱值;在这种情况下,根据第5节介绍中的第4段:“如果在表达式的评估过程中,结果不在其类型的可表示值范围内,则行为未定义。” - rici
在我看来,应该有一些指令来指定编译器必须选择特定的实现方式,例如signed-vs-unsigned char、signed-integer format等,或者如果编译器无法这样做,则产生编译错误。然后,以指令为开头的代码块将可移植到任何*使用二进制补码格式本地化的机器,或者到任何能够生成计算使用二进制补码值的代码的编译器所在的机器。 - supercat
请注意,因为charsigned charunsigned char是三种不同的类型,在char被视为有符号的编译器中,实际上是在编译一种与其为无符号时不同的语言。提供一种代码可以指定所需编译“语言”的方法将有助于防止由于将代码编译为与写作语言不一致的语言而导致的错误。 - supercat
@supercat:“可选有符号”字符并不是C语言中最好的想法,如果没有它们,这门语言可能会变得更好。但是那辆公共汽车在四十年前就已经开走了,现在也不会再回来了。 - rici
在sizeof(int)==1的环境中,char必须是有符号的。在源字符集中某些字符的表示超过了最大有符号char值的环境中,char必须是无符号的。我的观点是,如果代码只能在编译器满足某些期望的情况下才能正确工作,并且如果编译器可以符合不同的期望,那么有一种标准的方式请求编译器符合这些期望将是有用的。 - supercat

-1

结果不取决于机器如何表示整数。

0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0

上表显示了一个位的值。对于32位整数,所有32位的位计算都在进行中。因此术语是按位的。


是的,它确实可以。不仅适用于0和1,而且对于其他值,特别是负数,也是如此。 - Jan Hudec
OP在询问位运算,而不是逻辑运算。 - Ancurio
运算符 &、| 和 ^ 是按位运算符。运算符 && 和 || 是逻辑运算符。 - brian beuning

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接