【问题标题】:Bit Field Struct to int in C++位字段结构到 C++ 中的 int
【发布时间】:2018-09-28 14:52:50
【问题描述】:

我有一个包含相同类型成员的结构。我正在尝试将其转换为 uint8_t 类型。我能够做到这一点,但看不到输出,请告诉我哪里出错了。我也知道还有其他方法可以做到吗?我想这样做是因为我想习惯static_castreinterpret_cast

代码如下:

int main()
{
    struct xs{
        bool x :1 ;
        bool y :1;
        bool z :1;
        uint8_t num :5;
    } zs;

    uint8_t* P = static_cast<uint8_t*>(static_cast<void*>(&zs));

    cout << *P << endl;

    return 0;
}

【问题讨论】:

  • 您对 uint8_t 实际上是 unsigned char 的恼人事实感到不满,因此将被打印为一些 ascii 字符 - 很可能不可见。将其转换为其他整数类型。但是请记住,您正在做的事情(将类型视为不是的东西)是可怕的Undefined Behaviour,无法保证会发生什么。
  • @BoBTFish 用 char 类型试过还是没有输出
  • 正如@BoBTFish 所说,这是未定义的行为。 struct xs 的大小和布局是实现定义的,可能不是 uint8_t 的大小。即使是这样,你的演员所做的也被定义为不起作用。请参阅注释en.cppreference.com/w/cpp/language/bit_field
  • 只是一个警告:也许“习惯”重新解释演员表不是最好的主意。 reinterpret casts 用于特殊情况,不应该经常使用,实际上它们是危险信号,表明正在发生一些奇怪的事情。

标签: c++ c++11 visual-c++ bit-fields reinterpret-cast


【解决方案1】:
  1. 通过指向对象类型以外的类型的指针访问对象是未定义的行为。它适用于大多数编译器,但从技术上讲,您的程序可以为所欲为。

  2. 假设我们没有遇到 1 中提到的问题,我的猜测是 uint8_t 是 char 的别名,所以 cout 会将一个 char 放到你的控制台上。 您没有初始化您正在检查的内存,因此它可以为 0。值为 0 的字符不会在控制台中打印出任何“可观察”的内容,请查看 ascii 表。例如,尝试用 50 填充您的结构

【讨论】:

  • "你没有初始化你正在检查的内存,所以它可以是0"不,这将是未初始化的垃圾。
  • 我不认为是UB。 char*unsigned char*std::byte* 类型的指针有一个例外。
  • @JonathanWakely 所以技术上它可以是 0 :当然,根据标准它的垃圾。我最初想到的是一个编译器“扩展”,其中 int 仅在调试模式下初始化为零。我想我是在视觉工作室做的某处读到的?但我在这里可能大错特错。所以我让它打开到“它可以是 0”。
  • @Jens 你是对的。但是 uint8_t 总是(!)别名 char、unsigned char 还是 byte?
  • @phön 你是对的。 std::uint8_t 可以是任何正好是 8 位的类型,所以即使 CHAR_BITS==8 它也可能是别的东西。 char 也可以大于 8 位(这将使 sizeof 有趣,因为它以 sizeof(char) 的倍数测量),在这种情况下 std::uint8_t 没有什么不同。我在现实生活中从未见过这种情况。
【解决方案2】:

这里有两件事需要解决。首先,正如一些人指出的那样,您不能以这种方式访问​​您的对象。如果你想正确地构造一个 uint8_t,你需要读取你的结构中的变量并做一些位移,像这样:

uint8_t value = 0;
value |= (zs.x ? 1 << 7 : 0);
value |= (zs.y ? 1 << 6 : 0);
value |= (zs.z ? 1 << 5 : 0);
value |= zs.num;

现在,您面临的第二个问题是您尝试输出多个 8 位宽。默认情况下,这被解释为一个“字符”,并将显示为这样。为了完成您想做的事情,您可以使用不同长度的变量(uint16_t、uint32_t、...)或使用 std::to_string。

cout << std::to_string(value) << endl;

【讨论】:

    【解决方案3】:

    这里有很多问题:

    1. 您似乎相信xyz 将全部打包到单个uint_8 中。不是这种情况。 “然后,编译器可以将相同类型的相邻声明的位字段打包成数量减少的字”[1]
    2. sizeof(bool) 的值是实现定义的,可能与 1 不同”[2] 因此,您的 xs 将是实现定义的,但肯定不等于 sizeof(uint_8)
    3. 因为根据the rules defined for C++'s type aliasingxsuint_8 不“相似”,所以您的 reinterpret_cast&lt;uint_8*&gt; 的行为是未定义的
    4. 最后as has been pointed out by many others 看不到任何东西的原因是,无论实现定义的值是*P,当cout 将其视为char 时,它都可能是一个没有可见表示的控制字符

    您拥有的代码的一种可能的解决方法是使用这些定义:

    constexpr uint8_t X = 0B1000'0000;
    constexpr uint8_t Y = 0B0100'0000;
    constexpr uint8_t Z = 0B0010'0000;
    constexpr uint8_t NUM = 0B0001'1111;
    
    uint8_t zs;
    

    然后给zs分配一些值,您可以执行这些函数来输出以前的位字段:

    cout << ((zs & X) != 0) << endl;
    cout << ((zs & Y) != 0) << endl;
    cout << ((zs & Z) != 0) << endl;
    cout << (zs & NUM) << endl;
    

    Live Example

    【讨论】:

    • 谢谢你在“1”中描述的正是我想要的。我现在尝试实现 int* P = static_cast(static_cast(&zs));有用。但是,如果我将 x 定义为 true,则输出为 1,但如果我将 y 定义为 true,则输出为 2。它以何种顺序工作,谢谢。
    • 我想我知道它在 8421 规则中的工作,因为它基于位位置它给了我一个 int 数。但是当尝试在那里使用 char 而不是 int 时它不起作用。
    • 您使用#define 作为常量有什么原因吗?这似乎是一个初学者的问题,所以我不会展示你不应该使用的东西。
    • @Mandeep 恐怕至少我不明白你在说什么。什么是8421?你在哪里使用int的char insteaf?代码中没有int
    • @Jens 没有特别的原因。 const uint8_t x = 0B1000'0000#defines 一样好用
    猜你喜欢
    • 1970-01-01
    • 2014-11-07
    • 2014-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多