【问题标题】:Overflow on bitfield for signed underlying type enum有符号基础类型枚举的位域溢出
【发布时间】:2018-08-28 18:05:46
【问题描述】:

这仅适用于 C++11:

如果我有一个像下面这样的常规枚举:

enum TestType
{
Test0 = 0,
Test1,
Test2,
Test3,
Test4,
Test5,
Test6,
Test7
}

还有一个这样的打包结构:

struct
{
TestType a : 3
uint32_t b : 5
} TestStruct;

TestStruct.a 在访问时是否保证等于任何有效的分配枚举值?或者编译器是否有可能分配一个有符号的底层类型,然后将位域a 视为-4到3。

【问题讨论】:

  • "... 分配了枚举值?..." 什么值,TestStruct 尚未初始化..
  • 你可以拥有的最小基础类型是 (u)int8_t 所以我认为它不会改变任何东西,因为你的值是从 0 到 7。你不能有 -4,3 的范围,因为枚举的基础类型不受位域的影响。
  • 来自 cppreference on BitFields "无论是 char、short、int、long 和 long long 位字段,未显式签名或未签名的位字段是有符号的还是无符号的。例如,int b:3; 可能有值的范围 0..7 或 -4..3。” 因此,如果编译器选择底层类型为 int8_t,则位域可能只包含 -4 到 3 之间的值,这将导致Test4及以上要溢出?

标签: c++ c++11 enums bit-fields


【解决方案1】:

TestStruct.a 是否保证等于其分配的枚举值?

没有。您默认初始化TestStruct。如果这是在全局空间中,那么它将被初始化为零,ab 都将为 0。

如果这是在块空间中,则不会发生初始化,这意味着 ab 具有未指定的值。您所知道的是,该值将在该类型的可表示范围内。 Test0 的值为 0 根本不在这里发挥作用。

如果你有

TestStruct{};

然后ab 将为零,因为您正在初始化对象,在这种情况下,这意味着您将其初始化为零。你也可以使用

TestStruct{value1, value2};

ab 分配特定值。


对于a 是否可以存储TestType 的所有值的问题,我们必须查看[class.bit]/4 的哪些状态

[...]如果枚举数的值存储在相同枚举类型的位域中,并且位域中的位数足够大以容纳所有值 该枚举类型 ([dcl.enum]) 的原始枚举值和位域的值应比较相等。

强调我的

枚举的值由[dcl.enum]/8定义为

对于基础类型固定的枚举,枚举的值是基础类型的值。否则,对于 emin 是最小枚举数且 emax 的枚举 是最大的,枚举的值是范围内的值 bmin 到 bmax,定义如下: 令 K 为 1 表示二进制补码表示,0 表示补码或符号幅度表示。 bmax 是大于等于 max(|emin - K, |emax|) 且等于 2M-1,其中 M 是非负整数。如果 emin 为非负数,则 bmin 为零,否则为 -(bmax+K)。如果 bmin 为零,则足以容纳枚举类型的所有值的最小位域的大小为 max(M,1),否则为 M+1。可以定义一个枚举,它的任何枚举器都没有定义其值。如果 enumerator-list 为空,则枚举的值就好像该枚举有一个值为 0 的枚举数

所以在这种情况下,emin 为 0 且 emax 7 所以 bmin 为 0 且 bmax 等于或大于 max(|emin| - K, |emax|) 为 7。因为它必须等于 2 M-1 如果我们用 3 表示 M,那么我们也得到 7。

我们有

如果 bmin 为零,则足以容纳枚举类型的所有值的最小位域的大小为 max(M,1),否则为 M+1。

并且 bmin 为零,因此我们需要的最小位域是 3,因此您可以保证 TestType 的所有值都适合 a

【讨论】:

  • 这也取决于定义TestStruct的范围。在命名空间范围内,TestStruct.a 将为零。无论如何,问题似乎是关于枚举的基础类型,如果它已签名,是否可以用 3 位位域表示它。
  • @Praetorian 我已经更新了答案以包含它。我忘记了全局变量是零初始化的。
  • 对不起,我指的不是初始化后的值。我在问是否保证对任何有效枚举值的任何赋值(初始化程序、值赋值等)在访问位域时都会产生相同的值。
  • "您所知道的只是该值将在该类型的可表示范围内。"。该值是不确定的,使用它,未定义的行为。
  • @NathanOliver 下半部分是我一直在寻找的,听起来是正确的,我将答案标记为已接受。感谢您的洞察力。
猜你喜欢
  • 2012-09-15
  • 1970-01-01
  • 2016-11-30
  • 1970-01-01
  • 1970-01-01
  • 2019-08-29
  • 1970-01-01
  • 2012-03-09
  • 2010-10-25
相关资源
最近更新 更多