【问题标题】:Packing bools with bit field (C++)使用位域打包布尔值 (C++)
【发布时间】:2014-07-15 18:37:18
【问题描述】:

我正在尝试使用 C++ 与 Ada 代码交互,因此我正在使用位字段定义一个结构,以便所有数据在两种语言中都位于同一个位置。以下不是我正在做的事情,而是概述了问题。以下也是VS2008中的一个控制台应用程序,但不是很相关。

using namespace System;
int main() {
    int array1[2] = {0, 0};
    int *array2 = new int[2]();
    array2[0] = 0;
    array2[1] = 0;

    #pragma pack(1)
    struct testStruct {
        // Word 0 (desired)
        unsigned a : 8;
        unsigned b : 1;
        bool c : 1;
        unsigned d : 21;
        bool e : 1;

        // Word 1 (desired)
        int f : 32;

        // Words 2-3 (desired)
        int g[2]; //Cannot assign bit field but takes 64 bits in my compiler
    };
    testStruct test;

    Console::WriteLine("size of char: {0:D}", sizeof(char) * 8);
    Console::WriteLine("size of short: {0:D}", sizeof(short) * 8);
    Console::WriteLine("size of int: {0:D}", sizeof(int) * 8);
    Console::WriteLine("size of unsigned: {0:D}", sizeof(unsigned) * 8);
    Console::WriteLine("size of long: {0:D}", sizeof(long) * 8);
    Console::WriteLine("size of long long: {0:D}", sizeof(long long) * 8);
    Console::WriteLine("size of bool: {0:D}", sizeof(bool) * 8);
    Console::WriteLine("size of int[2]: {0:D}", sizeof(array1) * 8);
    Console::WriteLine("size of int*: {0:D}", sizeof(array2) * 8);
    Console::WriteLine("size of testStruct: {0:D}", sizeof(testStruct) * 8);
    Console::WriteLine("size of test: {0:D}", sizeof(test) * 8);

    Console::ReadKey(true);

    delete[] array2;
    return 0;
}

(如果不清楚,在实际程序中,基本思想是程序从与 Ada 代码通信的事物中获取 null* 并将其转换为 testStruct* 以访问数据,我认为.)

注释掉#pragma pack(1),输出为:

size of char: 8
size of short: 16
size of int: 32
size of unsigned: 32
size of long: 32
size of long long: 64
size of bool: 8
size of int[2]: 64
size of int*: 32
size of testStruct: 224
size of test: 224

显然 4 个字(索引 0-3)应该是 4*4*8 = 32*4 = 128 位,而不是 224。其他输出行是为了帮助确认 VS2008 编译器下的类型大小。

在未注释 #pragma pack(1) 的情况下,该数字(在输出的最后两行)减少到 176,仍然大于 128。似乎布尔值没有与中的无符号整数打包在一起“字 0”。

注意:a&b, c, d, e, f, 打包成不同的单词会是 5,数组 +2 = 7 个单词,乘以 32 位 = 224,我们用#pragma pack(1) 得到的数字被注释掉了.如果 c 和 e(布尔值)分别占用 8 位,而不是 32 位,我们得到 176,这是我们在未注释 #pragma pack(1) 时得到的数字。似乎#pragma pack(1) 只允许将布尔值自己打包成单个字节,而不是单词,但根本不允许将带有无符号整数的布尔值打包。

所以我的问题是一句话:有没有办法强制编译器将 a 到 e 打包成一个单词?相关的是这个问题:C++ bitfield packing with bools,但这并不能回答我的问题;它只是指出我试图强迫自己离开的行为。

如果实际上没有办法做到这一点,是否有人对解决方法有任何想法?我很茫然,因为:

  1. 我被要求避免更改我正在复制的结构格式(不重新排序)。
  2. 我不想将布尔值更改为无符号整数,因为它可能会导致问题不断地重新转换为布尔值,并且可能会意外使用错误版本的重载函数,更不用说使对于以后阅读它的其他人来说,代码更加晦涩。
  3. 我不想将它们声明为私有无符号整数然后创建公共访问器或其他东西,因为项目中所有其他结构的所有其他成员都可以直接访问而无需 (),所以它看起来有点笨拙和迟钝,并且几乎需要 IntelliSense 或反复试验来记住哪个需要 () 而哪个不需要。
  4. 我想避免为数据转换创建另一个结构类型(例如,为 testStruct 创建一个接受单个 testStructImport 类型对象的构造函数),因为实际的结构很长,有很多位- 字段指定的变量。

【问题讨论】:

  • 也许您可以更改 Ada 对应记录以匹配这些对齐方式?
  • 将该位域的所有成员设为私有并写入访问器 - 无论如何,这是一个实现细节。
  • 我会查找详细信息,但归根结底是不同类型的位域不会“合并”,并且每个新的位域类型都针对基础类型对齐。您需要将这些中的每一个存储为 unsigned 并酌情进行强制转换。
  • 另外,位域是按照实现定义的顺序,所以最好还是手动完成
  • 作为一种解决方法,我会尝试使用掩码和访问器。

标签: c++ struct pragma bit-fields pack


【解决方案1】:

我建议您创建一个没有任何位打包的“正常”结构。使用成员的默认 POD 类型。

创建用于从缓冲区 (uint8_t) 加载“普通”字段并存储到缓冲区的接口函数。

这将允许您在程序中以合理的方法使用数据成员。位打包和解包将由接口函数处理。位旋转应该使用按位与和按位或函数,而不是依赖于结构中的位字段表示法。这将允许您调整位旋转并在编译器之间更便携。

这就是我设计协议类的方式。而且我不必担心位域定位、字节序或类似的事情。

另外,我可以使用块 I/O 来读取和写入缓冲区。

【讨论】:

  • 谢谢,这是另一种解决方法。顺便说一句,uint8_t 在 Visual Studio 2008(我的 IDE,正如我提到的)中不存在。似乎是 C++11 的东西。
  • uint8_t 是 C99 的一部分并在“stdint.h”中定义,您可以至少更新到 Visual Studio 2010 或下载此头文件:see this question
  • @Étienne 是的,我尝试包含它,但 VS2008 找不到它。升级不是一种选择,因为我们是商业的,并且不想让我们的新项目与旧机器不兼容,所以感谢您的链接!
  • 或者您可以在 Visual Studio 2008 中使用 unsigned char
【解决方案2】:

尝试以这种方式打包:

    #pragma pack( push, 1 )
struct testStruct {
    // Word 0 (desired)
    unsigned a : 8;
    unsigned b : 1;
    unsigned c : 1;
    unsigned d : 21;
    unsigned e : 1;

    // Word 1 (desired)
    unsigned f : 32;

    // Words 2-3 (desired)
    unsigned g[2]; //Cannot assign bit field but takes 64 bits in my compiler
};
#pragma pack(pop)

【讨论】:

  • 哦,你编辑了它。是的,这行得通,因为它也可以更改pragma 行。我在我不喜欢的解决方法列表中的 #2 中解决了这个问题。
【解决方案3】:

没有使用访问器或接口层的简单、优雅的方法。不幸的是,没有像#pragma 这样的东西来解决这个问题。我最终只是将bools 转换为unsigned int 并重命名变量,例如ff_flagf_bool 鼓励正确使用并明确变量包含的内容。它比 Thomas 的解决方案省力,但显然没有那么健壮,并且仍然可以通过任何更简单的方法解决一些主要缺点。

【讨论】:

    【解决方案4】:

    在我发布此问题多年后,用户 @WaltK 将此评论添加到链接的相关问题中:

    "如果你想对位域的布局有更多的控制 内存中的结构,consider using this bit field facility, implemented as a library header file."

    【讨论】:

    • 我讨厌做一个几乎只是一个答案链接的答案,但它是一个很长的 PDF,我不能复制粘贴出来,所以我认为它没有意义引用整个答案。如果链接中断,请尝试搜索“C plus plus library bit fields”或github.com/wkaras的github用户。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-06
    • 1970-01-01
    • 2010-12-27
    • 1970-01-01
    • 2023-03-02
    • 1970-01-01
    相关资源
    最近更新 更多