table 偏移量的 C 递归宏

C Recursive Macros for table offsets

我们需要为外部存储器创建静态 table 地址。 存储结构的大小是已知且固定的。但是,这些结构的地址必须从内存部分的开头开始。 我们打算通过使用宏来做到这一点,因为让 table 随着 table 的元素在编译时发生变化而变化。

实现看起来像这样:

#define BLOC (0xFF)
#define FINAL_ADR(N) (ADR_##N + SIZE_##N)
#define GET_ADR(N) (FINAL_ADR(N) + (BLOC - (FINAL_ADR(N) & BLOC)))

#define ADR_0 (0x0)
#define SIZE_0 (5)

#define ADR_1 GET_ADR(0)
#define SIZE_1 (300)

#define ADR_2 GET_ADR(1)
#define SIZE_2 (130)
...

const struct {
   uint32_t addr, size;
} mem[] = {
  {.addr = ADR_0, .size = SIZE_0},
  {.addr = ADR_1, .size = SIZE_1},
  {.addr = ADR_2, .size = SIZE_2},
};

上面的代码尝试根据元素的先前地址和大小分配一个地址。

如果我们尝试实现上面的宏,编译器会失败。因为宏在元素 2 处引用了自身:

ADR_2 = ((GET_ADR(1)+ SIZE_1) + (0xFF- ((GET_ADR(1)+ SIZE_1) & 0xFF)))

是否可以绕过这个间接自引用宏?有没有更好的方法来完成这个?

您可以滥用枚举来获得它。

#include <stdint.h>

#define BLOC (0xFF)
#define FINAL_ADR(N) (ADR_##N + SIZE_##N)
#define GET_ADR(N) (FINAL_ADR(N) + (BLOC + 1 - (FINAL_ADR(N) & BLOC)))

enum {
  ADR_0 = 0x0,        SIZE_0 = 5,
  ADR_1 = GET_ADR(0), SIZE_1 = 300,
  ADR_2 = GET_ADR(1), SIZE_2 = 130,
};

const struct {
  uint32_t addr, size;
} mem[] = {
  {.addr = ADR_0, .size = SIZE_0},
  {.addr = ADR_1, .size = SIZE_1},
  {.addr = ADR_2, .size = SIZE_2},
};

编译为汇编程序源代码(x86_64):

        .text
        .section  .rdata,"dr"
        .globl    mem          # @mem
        .p2align  4
mem:
        .long     0            # 0x0
        .long     5            # 0x5
        .long     256          # 0x100
        .long     300          # 0x12c
        .long     768          # 0x300
        .long     130          # 0x82

哦,您的阻塞公式需要额外的 + 1 才能达到 0x100 的下一个倍数。

这不是嵌入式系统中内存映射变量的方式。

所有具有自定义内存映射的变量都在链接描述文件中配置,而不是在源代码中。您在链接描述文件中创建段(有时也称为节),您可以在其中指定每个段的起始地址和大小。

然后在源代码中,您使用一些特定于编译器的非标准扩展在您创建的段内分配变量。 gcc 示例:

struct element0 stuff __attribute__ (section ("ELEMENT_0")) = { ... };

现在如果代码的其他部分需要知道这个变量的地址,那么可以用&stuff.

获得

如果您的外部存储器是通过 SPI 等访问的串行存储器而不是内存映射,则只需使用(命名的)数字常量进行硬编码查找 table。不需要所有这些复杂的、不可读的宏。好的程序员写的代码越简单越好,而不是越复杂越好。