如何将结构数组初始化为非零值,如 0xFF?

How to initialize an array of structs to a value other than zero like 0xFF?

我有一个结构数组,需要在编译时初始化(无内存集) 为 0xFF。该数组将作为程序的一部分写入已擦除闪存。通过将其设置为 0xFF,它将在编程后保持擦除状态,应用程序可以将其用作持久存储。我找到了两种方法,一种是丑陋的,一种是解决方法。我想知道是否还有另一种语法方法我还没有找到。丑陋的方法是使用嵌套的初始化程序设置结构的每个字段。但是,它容易出错并且有点难看。我的解决方法是将结构分配为字节数组,然后使用结构类型的指针来访问数据。线性字节数组更容易初始化为非零值。

为了帮助其他人做同样的事情,我包括了使用的 gcc 属性和链接描述文件部分。

示例结构:

struct BlData_t {
    uint8_t version[3];
    uint8_t reserved;
    uint8_t markers[128];
    struct AppData_t {
        uint8_t version[3];
        uint8_t reserved;
        uint32_t crc;
    } appInfo[512] __attribute__(( packed ));
} __attribute__(( packed ));

使用我知道的最佳方式初始化为 0xFF:

// Allocate the space as an array of bytes 
// because it's a simpler syntax to 
// initialize to 0xFF.
__attribute__(( section(".bootloader_data") ))
uint8_t bootloaderDataArray[sizeof(struct BlData_t)] = {
    [0 ... sizeof(struct BlData_t) - 1] = 0xFF
};

// Use a correctly typed pointer set to the
// array of bytes for actual usage
struct BlData_t *bootloaderData = (struct BlData_t *)&bootloaderDataArray;

不需要初始化,因为 (NOLOAD):

__attribute__(( section(".bootloader_data") ))
volatile const struct BLData_t bootloader_data;

添加链接描述文件:

  .bootloader_data (NOLOAD):
  {
    FILL(0xFF);                  /* Doesn't matter because (NOLOAD) */
    . = ALIGN(512);              /* start on a 512B page boundary */
    __bootloader_data_start = .;
    KEEP (*(.bootloader_data))   /* .bootloader_data sections */
    KEEP (*(.bootloader_data*))  /* .bootloader_data* sections */
    . = ALIGN(512);              /* end on a 512B boundary to support
                                    runtime erasure, if possible */
    __bootloader_data_end = .;
    __bootloader_data_size = ABSOLUTE(. - __bootloader_data_start);
  } >FLASH

如何在代码中使用起始地址、结束地址和大小:

extern uint32_t __bootloader_data_start;
extern uint32_t __bootloader_data_end;
extern uint32_t __bootloader_data_size;
uint32_t _bootloader_data_start = (uint32_t)&__bootloader_data_start;
uint32_t _bootloader_data_end = (uint32_t)&__bootloader_data_end;
uint32_t _bootloader_data_size = (uint32_t)&__bootloader_data_size;

更新:

我会使用你的结构的 union 和一个正确大小的数组,然后初始化数组成员。

union {
    struct BlData_t data;
    uint8_t bytes[sizeof(struct BlData_t)];
} data_with_ff = {
    .bytes = {
        [0 ... sizeof(struct BlData_t) - 1] = 0xff
    }
};

然后您可以将您的结构作为 data_with_ff.data 访问,如果您愿意,可以定义一个指针或宏以方便使用。

Try on godbolt

(读者应该注意,指定初始化程序中的 ...a GCC extension; since the question was already using this feature and is tagged 我认为这里没问题。如果使用没有它的编译器,我不知道另一个.bytes = { 0xff, 0xff, 0xff ... } 之外的选项具有 0xff 的实际正确数量;您可能希望使用脚本生成它。)

明智的做法是在链接描述文件中找到命令,告诉它首先不要接触该内存。因为你为什么要擦除它只是为了再次填充 0xFF?那只会白白造成不必要的闪光磨损。

大致如下:

.bootloader_data (NOLOAD) :
{
  . = ALIGN(512);
  *(.bootloader_data *)
} >FLASH

如果您确实需要在纯标准 C 中进行此初始化,那么您可以将内部结构包装在匿名联合 (C11) 中,然后使用宏技巧初始化该结构:

struct BlData_t {
  uint8_t version[3];
  uint8_t reserved;
  uint8_t markers[128];
    
  union {
    struct AppData_t {
      uint8_t version[3];
      uint8_t reserved;
      uint32_t crc;
    } appInfo[512];

    uint8_t raw [512];
  };
};


#define INIT_VAL 0xFF,  // note the comma
#define INIT_1 INIT_VAL

#define INIT_2   INIT_1   INIT_1
#define INIT_5   INIT_2   INIT_2  INIT_1
#define INIT_10  INIT_5   INIT_5
/* ... you get the idea */
#define INIT_512 INIT_500 INIT_10 INIT_2

const struct BlData_t bld = { .raw = {INIT_512} };

此方法也可以应用于整个结构,例如,如果您想要初始化一个结构数组,并将所有项目设置为 compile-time 处的相同值。