打包结构体与联合体与枚举之间的区别

Difference between packing a struct vs union vs enum

提问人:ZeZNiQ 提问时间:10/8/2023 最后编辑:ZeZNiQ 更新时间:10/8/2023 访问量:95

问:

打包结构体、联合体和枚举有什么区别?

关于打包结构与打包联合,在 armv7l 上生成的汇编代码似乎有细微的差异(但在 x86_64 上没有):

#include <stdio.h>

struct uint18_struct {
        unsigned int var:18;
} __attribute__((packed));

typedef struct uint18_struct uint18_struct_t;

union uint18_union {
        unsigned int var:18;
} __attribute__((packed));

typedef union uint18_union uint18_union_t;

int main (void)
{
        unsigned int bar = ~0U;
        volatile uint18_struct_t foo = *((uint18_struct_t *) &bar);
        printf ("max of uint18_t = %u\n", foo.var);
        volatile uint18_union_t baz = *((uint18_union_t *) &bar);
        printf ("max of bf_u18_t = %u\n", baz.var);
        return 0;
}

为 foo 和 baz 生成的程序集:

        volatile uint18_struct_t foo = *((uint18_struct_t *) &bar);
   10358:   f107 020c   add.w   r2, r7, #12
   1035c:   f107 0308   add.w   r3, r7, #8
   10360:   8811        ldrh    r1, [r2, #0]
   10362:   7892        ldrb    r2, [r2, #2]
   10364:   8019        strh    r1, [r3, #0]
   10366:   709a        strb    r2, [r3, #2]

        volatile uint18_union_t baz = *((uint18_union_t *) &bar);
   1037a:   f107 020c   add.w   r2, r7, #12
   1037e:   1d3b        adds    r3, r7, #4
   10380:   8811        ldrh    r1, [r2, #0]
   10382:   7892        ldrb    r2, [r2, #2]
   10384:   8019        strh    r1, [r3, #0]
   10386:   709a        strb    r2, [r3, #2]

请注意生成的程序集(上图)中的前两行有何不同。ldrh

系统详细信息:

$ uname -a
Linux beaglebone 4.19.19-bone-rt-r21 #1stretch Wed Feb 6 11:01:34 UTC 2019 armv7l GNU/Linux
$ gcc --version
gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

我用的Cmdline:

$ gcc -g -O0 --save-temps main.c

您更愿意用哪一个来表示uint18_t类型 - 打包结构或打包联合?为什么?


编辑:想象一下,一个嵌入式控制器需要处理每个 9 位(但CHAR_BIT仍然是 8 位)的原始数据(因此需要 uint18_t),并且不提供 stdint.h、limits.h。

C 枚举 联合 结构-填料

评论

0赞 John Bollinger 10/8/2023
如果它遵循精确宽度整数类型 (C23 7.20.1.1/2) 的规范,则类型将正好用 18 位表示(并且没有填充)。这只有在具有 9 位或 18 位字符的 C 实现中才有可能,因为除位字段之外的每个对象的大小都是 大小的倍数(并且不能小于 8 位)。打包结构或联合不能解决这个问题 -- or 的大小必须是 .uint18_tcharcharstructunionchar
1赞 John Bollinger 10/8/2023
此外,它是一种 GNUism,所有其他用于管理打包的机制同样是特定于实现的。没有标准方法可以调节结构布局或联合大小。__attribute__((packed))

答:

0赞 chux - Reinstate Monica #1

您希望哪一个来表示uint18_t类型 (?)

struct uint18_struct {
  uint_least32_t var:18;
}

没有 .这种方式最便携。__attribute__((packed))

在 16 位系统上,希望它在位字段中接受 32 位 int 类型。int

否则,请考虑以下事项并自行处理子范围需求。

struct uint18_struct {
  uint_least32_t var;
}

IMO,OP 还没有提出需要包装的好理由。也许一旦更大的问题被陈述出来,我们就可以努力解决真正的问题。

评论

0赞 ZeZNiQ 10/8/2023
“IMO,OP还没有提出需要包装的好理由。也许一旦更大的问题被陈述出来,我们就可以努力解决真正的问题。谢谢你的建议。刚刚添加。更大的问题是这个系统不会提供 stdint.h。因此,这些类型需要由我们自己定义。
0赞 chux - Reinstate Monica 10/9/2023
@ZeZNiQ 什么版本的 C? 自 C99 起可用。可以使用<stdint.h>#if INT_MIN == -2147483648 typedef int my_int32_t #else ...
0赞 ZeZNiQ 10/9/2023
是的,这就是问题所在。这是一个裸机系统(想想在模拟器/FPGA 上验证的 ROM,还没有硅),我可能不得不自己定义这些标头。至少,我希望至少符合 C89,在这种情况下,limits.h 必须可用(以便您提出的INT_MIN方法可能有效),但不确定。这就引出了另一个问题 - 例如,有人可以在不提供 stdint.h 和 inttypes.h 的情况下声明 C99 兼容性,或者在不提供 limits.h 的情况下声明 C89 兼容性吗?
0赞 ZeZNiQ 10/9/2023
换句话说 - 有人可以声称部分符合特定版本的 C 标准吗?示例:符合 C99 且不带 stdint.h 和 inttypes.h,符合 C89 而不使用 limits.h 等。
1赞 ZeZNiQ 10/11/2023
__STDC__打印 1 和 __STDC_VERSION__ 错误,并显示“错误:__STDC_VERSION__未声明(首次使用此函数)”。