10

C++ 标准

如果C++14 实现在 a 的底层字节中包含填充位,unsigned int标准是否指定是否不得对填充位执行按位运算?

此外,C++14 标准是否指定相等和关系运算符是否必须忽略填充位?

指导方针

如果在这个问题上缺乏规范,是否对这些运算符在填充位上的预期行为达成某种共识?

我在 Stack Overflow 上发现了相互矛盾的答案。Orbitecatmur中的 Lightness Races说按位运算符不适用于算术,因为它们适用于所有位(包括填充位),而ChristophBartek Banachewicz说按位运算符处理整数的逻辑值并忽略填充。

参考

相关答案:关于填充位(123)的存在,关于缺乏明确的 C++ 规范(4)。

C++14中填充位的定义- § 3.9.1 - 基本类型:

对于窄字符类型,对象表示的所有位都参与值表示。对于无符号窄字符类型,值表示的所有可能的位模式都表示数字。这些要求不适用于其他类型。

C++14 中对象表示和值表示的定义 - § 3.9 - 类型:

类型对象的对象表示是类型对象占用的NT个对象的序列,其中N等于。对象的值表示是保存 type 值的一组位。对于普通可复制类型,值表示是对象表示中确定的一组位,该值是实现定义的一组值的一个离散元素。44 unsigned charTsizeof(T)T

脚注 44) 意图是 C++ 的内存模型与 ISO/IEC 9899 编程语言 C 的内存模型兼容。

C++14 中位与的定义 - § 5.11 - 位与运算符:

执行通常的算术转换;结果是操作数的按位与函数。该运算符仅适用于整数或无范围的枚举操作数。

C++14 中加法的定义 - § 5.7 - 加法运算符:

对算术或枚举类型的操作数执行通常的算术转换。此外,[...] 两个操作数都应具有算术或无范围枚举类型 [...]。二元+运算符的结果是操作数的和。

4

2 回答 2

4

首先,C++ 标准本身几乎没有提到填充位。基本上所有关于填充位的讨论都来自基本文档(即 C 标准)。

所以真正的问题是 C 标准对事物的描述。它的脚注 54 给出了一个相当简洁的填充位总结:

填充位的某些组合可能会生成陷阱表示,例如,如果一个填充位是奇偶校验位。无论如何,除了作为异常条件(如溢出)的一部分之外,没有任何对有效值的算术运算可以生成陷阱表示。填充位的所有其他组合都是由值位指定的值的替代对象表示。

运营商可能会改变一个大的填充。明显的情况是表示奇偶校验的填充位。如果您更改值的奇偶校验,奇偶校验位将更改为匹配。

“值的替代对象表示”部分基本上意味着只要您保持“在界限内”,填充位就不会影响您的结果。例如,如果比较两个值,则仅使用表示位来确定结果(6.2.6.1/4):

具有相同对象表示的两个值(NaN 除外)比较相等,但比较相等的值可能具有不同的对象表示。

您必须注意的时间和地点主要涉及未定义或实现定义的行为。例如,如果您将一个值存储到联合中的一个值中,然后在联合中检索另一个值,则第二个值可能会将填充位设置为陷阱表示,因此即使以这种方式查看值也可能会使您的程序(或其他)。

同样,如果您要取两个值,memcpy每个值到一个 unsigned char 缓冲区,这些字节的某些位可能比较不相等,即使它们表示的值确实比较相等。

即使您从不mempy直接使用它也会让您感到困惑的一个地方是一些比较和交换运算符。这些使用memcpyandmemcmp用于基础操作,因此它们也受到比较不相等的影响,即使所表示的值相等:

[atomics.types.operations]/23:

如果基础类型具有填充位、陷阱位或相同值的替代表示,则比较和交换操作的 memcpy 和 memcmp 语义可能会导致比较等于 operator== 的值比较失败。因此,使用 compare_exchange_strong 时应格外小心。另一方面, compare_exchange_weak 应该快速收敛。

旁注:两个大引号是描述性的,而不是规范性的——从规范性的角度来看,填充位几乎没有意义;几乎任何可能暴露填充位或其值的东西都涉及实现定义或未定义的行为。这里唯一的规范引用基本上是说:“填充位无效。”

于 2018-01-18T23:24:21.707 回答
0

如果实现为包含填充位的整数类型指定了存储格式,则它可以在写入对象时将任何它喜欢的内容写入这些位,并且可以对这些位必须保持的值施加它认为合适的任何要求,以任意方式表现如果不满足该要求,则受两个约束:

  1. 如果对这种类型的对象的任何写入都产生了特定的位模式,那么当从该类型的任何对象读取时,该位模式必须是可接受的,并且必须产生相同的值。

  2. 如果整数类型对象的所有位都为零,则该对象必须被视为有效并且必须读取为零。

如果实现忽略读取时的填充位,则按位运算符可能会影响它们,也可能不会以实现认为合适的任何方式影响它们。如果实现在多字节整数中的“1”位总数为奇数时进行陷阱,但始终写入使总奇偶校验为偶数的填充位值,则需要按位运算符来计算奇偶校验位在数据位上并适当地写入。

于 2018-01-18T23:15:38.863 回答