a和b的结果是什么?

39

有些尴尬,但是在C++标准中,按位与运算符定义如下(重点在于)。

进行通常的算术转换; 结果是其操作数的按位与函数。这个运算符仅适用于整数或无作用域枚举操作数。

对我来说,这看起来有点毫无意义。就我所知,在标准中没有定义“按位与函数”。

我理解“与函数”很容易理解,因此可能不需要解释。单词“按位”的含义应该也相当清楚:该函数应用于其操作数的相应位。但是,构成操作数的位不清楚。

怎么回事?


4
首先,这取决于“-”和“&”的相对优先级以及类型转换规则,在给定语言中需要仔细检查。但是,如果将-1解释为首先进行计算,在2的补码表示下,并且类型转换不改变值,则结果为0xFFFFFFFF&0x00000002,即2。 - Hot Licks
3
你认为随意做出那些C++没有规定的假设是可以的吗?实际上,你忽略了C++有规定的部分:一元减号比按位与运算符具有更高的优先级。 - R. Martinho Fernandes
2
@HotLicks:C语言没有规定二进制补码,C++好像也是这样。 - Kevin
3
重点在于,计算机科学专业的学生是否对“按位与”有一个大致的概念并不重要。实际上,如果他们知道“按位与”的意思,我期望他们能够发现未明确说明的部分,或者至少意识到他们正在做出未指定的假设。 - R. Martinho Fernandes
3
不,标准并没有规定负数要使用二进制补码表示法。但它确实规定了正数需要使用二进制表示法。也就是说,9始终是1001,3始终是11,因此9和3的按位与操作始终为11。不需要使用格雷码或BCD码。 - Lee Daniel Crocker
显示剩余5条评论
4个回答

39
这是未明确说明的。当标准提到位操作时,其含义是一些缺陷报告的主题。
例如缺陷报告1857: 关于位的其他问题
规范中5.11[expr.bit.and]、5.12[expr.xor]和5.13[expr.or]中的位操作使用了未定义的术语“位操作”,但没有指定是在考虑值还是对象表示。
解决的一部分可能是将“位”(在C++中否则当前未定义)定义为给定2的幂的值。
回复如下:
CWG决定重新制定操作本身的描述,以避免对位的引用,将定义“位”等更大的问题拆分到问题1943中进行进一步考虑。

并且缺陷报告1943说:

CWG在2014-06(Rapperswil)会议上决定仅解决1857和1861问题引发的一小部分问题。本问题是剩余问题的占位符,例如通过2 n 的值定义“位”,指定位字段是否具有符号位等。

从这个缺陷报告1796:对于空字符,“所有位都为零”是否是有意义的要求?可以看出,标准引用位时所指的意思也影响到其他部分:

根据2.3 [lex.charset]第3段,基本执行字符集和基本执行宽字符集应包含基本源字符集的所有成员,以及表示警报、退格和回车的控制字符,加上一个空字符(分别是 null 宽字符),其表示具有所有零位。不清楚便携式程序是否可以检查表示的位;相反,似乎只能检查对应于值表示的数字的位(参见3.9.1 [basic.fundamental]第1段)。更合适的做法可能是要求空字符值等于0或'\0',而不是指定表示的位模式。对于移位、按位与和按位或运算符的定义也存在类似问题:这些规范是关于表示的位模式的约束还是关于将这些模式解释为数字后产生的值的约束?在这种情况下,解决方案是将“表示具有所有零位”更改为“值为0”。
请注意,正如ecatmur的回答中所提到的,草案C++标准在第3.9.1[basic.fundamental]的第3段中确实参照了C标准的第5.2.4.2.1节,但它并未参考C标准的第6.5/4节,后者至少可以告诉我们结果是实现定义的。我在下面的评论中解释了C++标准只能明确地包含规范参考文本。

你能否评论其他人提到的[basic.fundamental]/7吗?在我看来,这个规定确立了行为。值表示中的值或符号位必须对应于对象表示中的位,因此无论您认为操作适用于哪种表示,都没有关系。 - M.M
@MattMcNabb 我认为其他两个答案已经说得够多了。我认为它没有回答负数的问题,除非可能应该是实现定义。请注意,我们不能从C99引用6.5/4,因为它在C++标准中没有明确引用,请参见Can we apply content not explicitly cited from the normative references to the C++ standard? - Shafik Yaghmour
1
如果标准提供了更强的保证,那么缺陷报告就会显得毫无意义。 - Shafik Yaghmour
关于与空字符相关的缺陷报告,我认为标准规定清除整数类型的所有位必须产生合法的零值表示,但不一定是唯一的。因此,我认为标准的改进放宽了限制,但这种方式并不能让编译器作者受益。额外的灵活性如何使编译器受益呢? - supercat
据我所知,这是一种避免处理“位(bit)”未正确定义的方法,“df 1943”的解决方案应该能够解决这个问题。@supercat - Shafik Yaghmour
如果问题在于在这种情况下“bit”没有真正被定义,那么将“all bits zero”更改为“all bytes zero”应该可以解决这个问题。否则,一个字符串字面量的结尾可能没有用任何零值char标记[例如,因为wchar_t具有由编译器设置的填充位],这是合法的。 - supercat

9

[basic.fundamental]/3指的是C++标准中基本概念部分的第3节,与C 5.2.4.2.1相对应。由于C++中位运算符的不充分规定,因此在这种情况下,应该类似地参考C标准6.5.10/4:

二进制 & 运算符的结果是操作数的按位AND(即,仅当转换后的操作数的相应位均设置时,结果中的每个位才设置)。

请注意,C 6.5/4中有:

某些运算符(一元运算符 ~ 和二元运算符 <<、>>、&、^ 和 |,统称为“位运算符”)需要具有整数类型的操作数。这些运算符产生依赖于整数的内部表示的值,并且对于有符号类型,具有实现定义和未定义的方面。

整数的内部表示当然在6.2.6.2/1、/2中描述。


似乎是合理的,但实际上并没有明确说明。这就是问题所在。 - Lightness Races in Orbit
为什么要踩?考虑到这个问题没有具体说明,这是一种合理且信息丰富的思考方式。++1。 - Shafik Yaghmour
@ShafikYaghmour:请看我的评论。这并没有回答问题。 - Lightness Races in Orbit
我之前错过了这个:being underspecified should similarly defer to C, in this case 6.5.10/4 ...除非明确规定,否则C++标准不能推迟到C标准,请参见此处。尽管我可以自信地说这可能是意图。 - Shafik Yaghmour

6
C++标准将“存储”定义为一定数量的比特。实现可能决定为特定位赋予什么含义;也就是说,二进制AND应该基于形成特定类型表示的概念0和1进行操作。
3.9.1.7. (...) 整数类型的表示必须使用纯二进制计数系统来定义值。49 (...)
3.9.1, footnote 49) 一种使用二进制数字0和1的整数位置表示法,在其中由连续位表示的值是可加的,以1开头,并乘以连续的2的幂,除了最高位可能不是这样。
这意味着,无论使用什么物理表示,二进制AND都按照AND函数的真值表进行操作(对于每个位号i,从适当的操作数中取出Ai和Bi位,并仅在两者都为1时产生值1,否则为位Ri产生0)。结果值的解释留给实现来处理,但无论选择什么,它都必须符合其他二进制操作(如OR和XOR)的期望。

3

从法律上讲,我们可以认为所有按位操作都具有未定义的行为,因为它们实际上没有定义。

更合理的做法是运用常识并参考这些操作的常见含义,将其应用于操作数的(因此称为“按位”)。

但事实上并没有明确规定。遗憾的是,我的答案不能被视为规范措辞。


6
抱歉格式不佳...在3.9.1中我们可以找到这样的内容:整数类型的同义词是整型。整数类型的表示应该使用纯二进制数字系统来定义值。 纯二进制数字系统在脚注中被定义为:用二进制数字0和1表示位值、从1开始相加、每个比特位乘以连续的2的幂次方,除了最高位可能不乘以2的幂次方的整数的位置表示法。 - fjardon
1
@fjardon:那个最终子句非常重要,不是吗? - Lightness Races in Orbit
2
是的,但我看不出它如何允许(0 | 5)成为除5以外的其他值,因为int类型有超过4位。我不是说你错了,我只是想理解。 - fjardon
@fjardon:回想起来,那可能不是最好的例子。 - Lightness Races in Orbit

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