【问题标题】:Iterate members of a bitfield迭代位域的成员
【发布时间】:2010-11-08 19:42:31
【问题描述】:

我们有这个例子:

struct X {
  int e0 : 6;
  int e1 : 6;
  int e2 : 6;
  ...
  int e10 : 6;
};

struct X c;

如何“自动”访问成员,例如:
c.e{0-10} ?
说如果我想读 c.e0,然后 c.e1 ...
如果我的结构有 1000 个元素,我认为我不应该写这么多代码,对吧?

你能帮我一个解决方法,一个想法吗?
我提到我已经阅读了与此问题相关的其他帖子,但我没有找到解决方案。

非常感谢!

【问题讨论】:

  • 在这里使用结构的商业案例是什么?任何其他数据元素也可以使用吗?
  • 你真的需要 6 位 int 成员吗?这会影响问题的答案。
  • 很抱歉通知您,但如果您的 struct would have 1000 elements 比您的设计严重损坏...
  • 结构体的长度必须是三个值,超过四个可以使用这些结构体的数组。
  • 谢谢大家的回答!这是一个应该模拟 SET Union 和 Intersection 的问题。我的想法是我应该使用这样的结构。假设 e0 中我应该拥有的最大数字是 32。这就是为什么我的位字段为 6。问题中未指定的是 SET 将具有多少个元素。即使我们有一个 SET A 和另一个,B 和 A 有 5 个元素,B 有 3 个,它仍然不方便(因为我必须写一些代码行)。

标签: c++ bit-fields


【解决方案1】:

正如其他人所说,你不能完全用位域做你想做的事。看起来您想以最大的空间效率存储大量 6 位整数。我不会争论这是否是一个好主意。相反,我将展示一种类似老式 C 的方法,使用 C++ 特性进行封装(未经测试)。这个想法是 4 个 6 位整数需要 24 位或 3 个字符。

// In each group of 3 chars store 4 6 bit ints
const int nbr_elements = 1000;
struct X
{
    // 1,2,3 or 4 elements require 3 chars, 5,6,7,8 require 6 chars etc.
    char[ 3*((nbr_elements-1)/4) + 3 ] storage;
    int get( int idx );
};

int X::get( int idx )
{
    int dat;
    int offset = 3*(idx/4);      // eg idx=0,1,2,3 -> 0 idx=4,5,6,7 -> 3 etc.
    char a = storage[offset++];
    char b = storage[offset++];
    char c = storage[offset];
    switch( idx%4)  // bits lie like this; 00000011:11112222:22333333
    {
        case 0: dat = (a>>2)&0x3f;                    break;
        case 1: dat = ((a<<4)&0x30) + ((b>>4)&0x0f);  break;
        case 2: dat = ((b<<2)&0x3c) + ((c>>6)&0x03);  break;
        case 3: dat = c&0x3f;                         break;
    }   
    return dat;
}

我将把配套的 put() 函数留作练习。

【讨论】:

  • 我会使用template&lt;std::size_t N&gt; struct X { char[3*((N - 1) / 4) + 3] storage; }; 来调整元素的数量,但这可能是必要的,也可能不是必要的。另外,我会添加一个检查以确保索引在范围内。
  • @bsabin 不客气。如果您希望我也写 put(),请在 cmets 中询问。我只是注意到我已经有一个类似的答案,并且有人评论说写(放置)将是“地狱般的”。我相信它实际上不会太糟糕。
  • @Chris。感谢您的建议,正如您所说,我更像是一名 C 程序员,但我正在尝试更多地使用 C++,我可以向像您这样的 cmets 学习。
  • @Bill - 我通常更喜欢 C 而不是 C++,但我越习惯(有时是可怕的)语法,我就越学会欣赏 C++ 的(一些)特性。我发现老式 C hack 和宏与 C++ 运算符重载的融合非常有趣,并且可以产生一些不错的新(并且非常清晰)语法:phimuemue.com/blog.php?article=172(不是我的,但仍然很棒)跨度>
  • @Chris。这个例子非常酷,非常适合个人项目,你可以接受这些东西并将其真正整合到你的心智模型中。但是,当然,如果您将类似的东西添加到一个大项目中,您可能会为社区制造一场维护噩梦。 Linus Torvalds 喜欢抱怨 C++ 的这一方面,他的例子比你的例子要少得多。有趣的是,现代 C++ 功能可以创建显然非常好的代码,除了任何尝试将代码分开并理解它的尝试淹没在一百万个 字符之外。
【解决方案2】:

听起来结构不是您想要做的事情的正确工具。您需要arrayvector。数组用于存储多个相同类型的数据。向量是自动管理项目的添加和删除的数组包装器。

如果您需要相同数据的列表和一些其他数据(比如字符串),您可以将数组或向量作为结构的一部分。

struct X {
   int[10] numbs;
   string name;
};

X c;

【讨论】:

    【解决方案3】:

    你不能。为此需要某种形式的反射,而 C 或 C++ 都不支持。

    【讨论】:

      【解决方案4】:

      由于您的位域大小相同,您可以封装std::bitset(或vector&lt;bool&gt;,gulp...)并提供您自己的迭代器(每个增量移动书签六位)和operator[](等)让您的代码更易于编写。

      我确信与位域相比性能会很差。

      【讨论】:

        【解决方案5】:

        这样的事情怎么样:

        char getByte(char *startPos, int index) {
        
            int i = (index*6) / 8;
        
            if (index % 4 == 0)
                return 0b11111100 & startPos[i] >> 2;
            else if (index % 4 == 3)
                return 0b00111111 & startPos[i];
            else if (index % 4 == 2)
                return (0b00001111 & startPos[i] << 2) | (0b11000000 & startPos[i+1] >> 6);
            else
                return (0b00000011 & startPos[i] << 4) | (0b11110000 & startPos[i+1] >> 4);
        }
        

        【讨论】:

        • 只是好奇 - 你测试过这个吗?通过这种方式读取访问似乎可行,但写入访问将是地狱般的。
        • 很确定应该是index % 4 == 3 而不是index % 3
        • @Billy、@Chris、@Gaz:我很确定位域的底层类型在是否允许打包方面发挥了作用。例如如果这是问题中的 16 位 int,则允许编译器将 2 个 6 位字段和 4 位填充放在 int 中,但不允许使用 32 位 int ,编译器将不得不将 5 个 6 位字段加上 2 位填充放在一起以生成 int
        • @Ben @Chris @Gaz:见 open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf 的 6.2.6.1。目前还不清楚是否允许。 6.7.2.1 第 10 条指出,如果位域可以放入单个可寻址单元(通常为char),编译器必须将其放入该单个可寻址单元,但如果位域为太宽了,装不下一个单元。
        • @Gaz:我认为一些位移也可能是有序的。
        猜你喜欢
        • 2011-05-26
        • 2023-03-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-24
        • 2018-08-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多