如何可视化内存中带有位字段的C结构的布局?

如何可视化内存中带有位字段的C结构的布局?,c,struct,C,Struct,我正在使用C结构,包括如下位字段: struct beeper_general_t { uint1_t enable : 1; uint32_t : 7; enum2_t loudness : 2; uint32_t : 22; enum2_t status : 2; }; struct beeper_general_t my_struct; for(;;) { my_struct.enable = 0; my_struct.enable = 1; } 出

我正在使用C结构,包括如下位字段:

struct beeper_general_t
{
  uint1_t enable : 1;
  uint32_t : 7;
  enum2_t loudness : 2;
  uint32_t : 22;
  enum2_t status : 2;
};
struct beeper_general_t my_struct;
for(;;) {
    my_struct.enable = 0;
    my_struct.enable = 1;
}
出于调试目的,我需要知道编译器(GCC)如何在内存中布局结构(每个字段的确切位置和宽度)

我现在正在做的是编写一些测试代码,如下所示:

struct beeper_general_t
{
  uint1_t enable : 1;
  uint32_t : 7;
  enum2_t loudness : 2;
  uint32_t : 22;
  enum2_t status : 2;
};
struct beeper_general_t my_struct;
for(;;) {
    my_struct.enable = 0;
    my_struct.enable = 1;
}

然后,我查看生成的汇编代码以获得所需的信息。由于这是一个相当繁琐的过程,我想知道是否有一种更简单的方法来可视化内存中结构的实际布局。

我希望所有类型的
uint1\u t
uint32\u t
等都能正确利用位字段。 如果不是,则应将其设置为:

struct beeper_general_t
{
  uint32_t enable : 1;
  uint32_t : 7;
  uint32_t loudness : 2;
  uint32_t : 22;
  uint32_t status : 2;
};
6.7.2.1结构和联合规范

[10] 一个实现可以分配任何大的可寻址存储单元 足够容纳一个位字段。如果剩余的空间足够大,则需要一个位字段 结构中紧跟其后的另一个位字段应打包 插入同一单元的相邻位。如果空间不足, 是否将不适合的位字段放入下一个单元或 重叠相邻单元由实现定义。位字段的分配顺序 单元内(高阶到低阶或低阶到高阶)为 实现定义。可寻址存储单元的对齐 没有具体说明

如果你想要一个 具体布局,请优先选择位移位,因为位字段的布局是 未指定,可能随编译器、目标等而更改


如果您希望启用为1位,响度为2位等,则使用位字段是明智的选择。如果您希望启用位为0或31或类似要求,则应避免使用位字段。

生成的程序集或编译器文档是实现此目的的典型方法。写一个函数来转储一个结构所占用的内存以及所有成员的地址偏移量并不是什么大不了的事,但是对于每个新的结构,都需要重新执行这个函数。

当前版本的C标准对位域布局的定义过于严格,不允许编译器对其进行最佳布局,但是没有足够的细节来允许代码做很多事情,如果标准没有提到它们的布局

类似于
无符号短x:5的定义
表示如果没有前一个结构元素,则前一个结构元素不是声明类型为
short
unsigned short
的位字段,或者容纳它的空间没有空间容纳另一个五位值,编译器应创建类型为
无符号短
的新未命名结构元素,并将五个连续位的范围分配给
x
。如果这些条件都不适用(即存在一个前一个结构元素,它是一个声明类型为
short
unsigned short
的位字段,并且容纳它的空间可以容纳另一个五位位位字段),则该位字段将与前一个位字段共享该元素

因此,给出如下声明:

struct {
  uint16_t a,b,c,d,e,f,g,h : 6;
} PACK_AS_UINT16;
编译器首先将
a
放在一个16位字中,然后将
b
放在同一个字的某个地方。由于
c
不能放在同一个单词中(这将使总数达到18位),编译器必须为
c
分配另一个单词,但也可以将
d
打包在其中。同样,另一个词表示
e
f
,第四个词表示
g
h
。如果声明是:

struct {
  uint32_t a,b,c,d,e,f,g,h : 6;
} PACK_AS_UINT32:
然后,编译器将能够将
a
-
e
打包到一个32位字中,并将
f
-
h
放入另一个32位字中(在第二个字中留下14位未使用的空间)

请注意,虽然八个6位值的最佳打包将使用三个16位字,但这种打包将要求其中两个值跨16位项。尽管一些编译器在历史上能够毫不费力地适应这种情况,但目前的标准不允许这样做。

对于查看结构布局非常有用。遗憾的是,因为
uint1\u t
enum2\u t
不是标准类型,我必须在中替换其他类型,但下面是一些示例输出:

struct beeper_general_t
{
        uint32_t enable : 1;
        uint16_t : 7;
        uint32_t loudness : 2;
        uint64_t : 22;
        uint32_t status : 2;
};
struct beeper_general_t {
    uint32_t                   enable:1;             /*     0:31  4 */
    uint32_t                   loudness:2;           /*     0:22  4 */

    /* XXX 29 bits hole, try to pack */

    uint32_t                   status:2;             /*     4:30  4 */

    /* size: 8, cachelines: 1, members: 3 */
    /* bit holes: 1, sum bit holes: 29 bits */
    /* bit_padding: 30 bits */
    /* last cacheline: 8 bytes */
};
此pahole输出的结果:

struct beeper_general_t
{
        uint32_t enable : 1;
        uint16_t : 7;
        uint32_t loudness : 2;
        uint64_t : 22;
        uint32_t status : 2;
};
struct beeper_general_t {
    uint32_t                   enable:1;             /*     0:31  4 */
    uint32_t                   loudness:2;           /*     0:22  4 */

    /* XXX 29 bits hole, try to pack */

    uint32_t                   status:2;             /*     4:30  4 */

    /* size: 8, cachelines: 1, members: 3 */
    /* bit holes: 1, sum bit holes: 29 bits */
    /* bit_padding: 30 bits */
    /* last cacheline: 8 bytes */
};
类似问题:


-g
传递给GCC,以便它生成调试信息如何?如何获得有关结构布局的信息?编译每行打印一个结构字段的代码,然后使用
objdump-S
反编译。它将包含结构的每个字段的偏移量。谢谢,但我的问题更多的是关于如何获取编译器选择的结构布局的信息。我很清楚,几乎所有关于位字段的内容都依赖于实现,通常应该避免它们。好的,那么我更喜欢方法1:在我的_结构周围转储内存(在程序中或使用GDB),方法2:使用
-S
开关,通过增量设置位字段来分析汇编中的差异。由于位字段没有地址,因此很难检索地址偏移量。