Struct 在现代C(C99/C11联合)中使用位字段作为排序键

Struct 在现代C(C99/C11联合)中使用位字段作为排序键,struct,bit-manipulation,c99,bit-fields,c11,Struct,Bit Manipulation,C99,Bit Fields,C11,要求: 对于我的微型图形引擎,我需要一个所有对象的数组来绘制。出于性能原因,需要根据属性对该数组进行排序。简言之: 每个结构存储大量属性,将结构添加到结构数组中 高效地对数组进行排序 浏览阵列并根据属性执行操作(模式设置和绘图) 方法:并集中的位字段(即:让编译器为我进行掩蔽和移位) 基于这篇文章,我认为我有一个很好的计划来实现这一点。其思想如下:每个属性都是一个位字段,可以对其进行读取和写入(步骤1)。写入后,排序过程将位字段结构视为整数,并对其进行排序(步骤2)。之后(步骤3),再次读取位字

要求

对于我的微型图形引擎,我需要一个所有对象的数组来绘制。出于性能原因,需要根据属性对该数组进行排序。简言之:

  • 每个结构存储大量属性,将结构添加到结构数组中
  • 高效地对数组进行排序
  • 浏览阵列并根据属性执行操作(模式设置和绘图)
  • 方法:并集中的位字段(即:让编译器为我进行掩蔽和移位)

    基于这篇文章,我认为我有一个很好的计划来实现这一点。其思想如下:每个属性都是一个位字段,可以对其进行读取和写入(步骤1)。写入后,排序过程将位字段结构视为整数,并对其进行排序(步骤2)。之后(步骤3),再次读取位字段

    有时代码会说1000多个字,这是一个高级视图:

    联合键{
    /*用于访问*/
    结构{
    unsigned int some_attr:2;
    无符号整数另一个属性:3;
    /* ... */
    }比特雷普;
    /*用于排序*/
    uint64_t intrep;
    };
    
    我只想确保位表示与整数表示(本例中为64位)一样大。我的第一个方法是这样的:

    联合键{
    /*用于访问*/
    结构{
    /*通用部分:11位*/
    无符号整数层:2;
    无符号整数视口:3;
    未签名的int-viewportLayer:3;
    无符号整数半透明:2;
    无符号整数类型:1;
    /*取决于类型位:64-11位=53位*/
    联合{
    结构{
    无符号整数序列:8;
    无符号整数id:32;
    无符号整数填充:13;
    }cmd;
    结构{
    无符号整数深度:24;
    无符号整数材质:29;
    }正常;
    };
    };
    /*用于排序*/
    uint64_t intrep;
    };
    
    注意,在这种情况下,有一个名为
    type
    的决策位字段。在此基础上,填充cmd结构或普通结构,就像前面提到的文章一样。然而,这个失败得可怕。OSX 10.9(x86 macbook pro)上的clang 3.3的密钥联合是16字节,而它应该是8字节

    由于无法强制clang更好地打包结构,我采用了另一种基于其他堆栈溢出答案和预处理器的方法,以避免重复自己的操作:

    /*2+3+3+2+1+5=16位*/
    #定义泛型字段\
    无符号整数层:2\
    无符号整数视口:3\
    未签名的int-viewportLayer:3\
    无符号整数半透明:2\
    无符号整数类型:1\
    无符号整数:5;
    /*8+32+8=48位*/
    #定义命令字段\
    无符号整数序列:8\
    无符号整数id:32\
    无符号整数:8;
    /*24+24=48位*/
    #定义模型字段\
    无符号整数深度:24\
    无符号整数材质:24;
    结构泛型{
    /*16位*/
    通用字段
    };
    结构命令{
    /*16位*/
    通用字段
    /*48位*/
    命令字段
    }_uuu属性_uuu((压缩));
    结构模型{
    /*16位*/
    通用字段
    /*48位*/
    模型字段
    }_uuu属性_uuu((压缩));
    联合碱{
    结构泛型gen;
    结构命令cmd;
    结构模型模型;
    uint64_t intrep;
    };
    
    如果不包括
    \uuu属性((打包))
    命令和
    模型结构为12字节。但是有了
    \uuuuuu属性((打包))
    ,它们是8个字节,这正是我想要的!看来我已经找到了解决办法。然而,我对比特域的小小经验让我学会了谨慎。这就是为什么我有几个问题:

    我的问题是:

    • 我可以让它更干净(例如:更像我在union中的struct中的第一个大union),并且仍然保留8字节作为键,以便快速排序吗
    • 有没有更好的方法来实现这一点
    • 这安全吗?这在x86/ARM上会失败吗?(真正异国情调的建筑不太值得关注,我的目标是两种最流行的建筑)。设置一个位字段,然后发现一个相邻的位字段已经被写入。不同的编译器在这方面会有很大的差异吗
    • 我可以从不同的编译器中得到什么问题?目前,我的目标是使用
      -std=c11
      ,实现clang3.3+和gcc4.9+。不过,如果我将来也能使用MSVC,那就太好了
    我查阅过的相关问题和网页:

    • 对于那些(像我一样)对非字节对齐和endianness的位字段所发生的情况摸不着头脑的人,请不要再往下看:
    遗憾的是,没有人回答我,我一路走到那里


    编辑:实验时,设置一些值并读取整数表示。我注意到一些我已经忘记的东西:持久性。这又打开了一罐虫子。甚至可以使用位字段执行我想要的操作,还是必须执行位移位操作?

    位字段的布局高度依赖于实现(=编译器)。本质上,如果编译器认为合适或不合适,它可以在同一字节/字中自由放置连续的位字段。因此,如果没有像您提到的
    packed
    属性这样的扩展,您永远无法确保您的位字段被压缩到一个单词中

    然后,如果没有将位字段压缩到一个字中,或者如果您只有一些不使用的备用位,那么您可能会遇到更大的麻烦。这些所谓的填充位可以具有任意值,因此您的排序思想在可移植设置中永远不会起作用

    由于所有这些原因,位字段在实际代码中很少使用。你能经常看到的是使用