gcc 初始化一个结构体和匿名结构体有什么区别?

xurqigkl  于 2022-11-12  发布在  其他
关注(0)|答案(1)|浏览(174)

请考虑以下代码:

typedef struct {
  uint32_t a : 1;
  uint32_t b : 1;
  uint32_t c : 30;
} Foo1;

void Fun1(void) {
  volatile Foo1 foo = (Foo1) {.a = 1, .b = 1, .c = 1};
}

在嵌入式应用程序中使用位字段来穿孔寄存器时,经常会出现这种通用模式。使用最近的ARM gcc编译器(例如gcc 8.2或gcc 7.3),通过-O3-std=c11,我得到了下面的程序集:

sub sp, sp, #8
  movs r3, #7
  str r3, [sp, #4]
  add sp, sp, #8
  bx lr

这几乎正是您想要和期望的; Foo不是易失性的,因此在最终存储到易失性变量(寄存器)foo之前,可以将每个位的初始化一起组合到文字0x7中。
然而,能够操作整个寄存器的原始内容是很方便的,这会产生位字段的匿名实现:

typedef union {
  struct {
    uint32_t a : 1;
    uint32_t b : 1;
    uint32_t c : 30;
  };
  uint32_t raw;
} Foo2;

void Fun2(void) {
  volatile Foo2 foo = (Foo2) {.a = 1, .b = 1, .c = 1};
}

遗憾的是,最终的装配并不是如此优化:

sub sp, sp, #8
  ldr r3, [sp, #4]
  orr r3, r3, #1
  str r3, [sp, #4]
  ldr r3, [sp, #4]
  orr r3, r3, #2
  str r3, [sp, #4]
  ldr r3, [sp, #4]
  and r3, r3, #7
  orr r3, r3, #4
  str r3, [sp, #4]
  add sp, sp, #8
  bx lr

对于密集的寄存器,每一位的读取-修改-写入操作可能会变得......昂贵。
union / anonymous结构体有什么特殊之处,它阻止gcc像pure结构体一样优化初始化?

zysjyyx4

zysjyyx41#

我希望我能回答你的问题。问题是GCC编译器有一些关于C到汇编程序转换的预定义规则,当你用一个struct和一个uint32_t创建一个union时,它没有预定义的模式,这就是为什么最终的汇编程序没有像第一个例子那样优化。
我建议你用铸造来解决这个问题。

typedef struct {
  uint32_t a : 1;
  uint32_t b : 1;
  uint32_t c : 30;
} Foo1;

void Fun1(void) {
  volatile Foo1 foo = (Foo1) {.a = 1, .b = 1, .c = 1};
  volatile uint32_t rawA = *((uint32_t *) &foo);
  volatile uint32_t rawB = *((uint32_t *) &foo + sizeof(uint32_t);
}

相关问题