什么是编译器友好和endian不可知的方式来写这个?

时间:2015-01-16 07:05:42

标签: c memory endianness

我在嵌入式系统上有一些代码。代码可以在link看到,特别是此代码段:

uint32_t raw_fusebits[2];
....
/* Read the fuse settings in the user row, 64 bit */
((uint16_t*)&raw_fusebits)[0] = (uint16_t)NVM_MEMORY[NVMCTRL_USER / 2];
((uint16_t*)&raw_fusebits)[1] = (uint16_t)NVM_MEMORY[(NVMCTRL_USER / 2) + 1];
((uint16_t*)&raw_fusebits)[2] = (uint16_t)NVM_MEMORY[(NVMCTRL_USER / 2) + 2];
((uint16_t*)&raw_fusebits)[3] = (uint16_t)NVM_MEMORY[(NVMCTRL_USER / 2) + 3];

这会导致C99下的编译器警告与严格别名相关。有没有更好的方法来写这个?起初我想(使用一些更简单的变量名来提高可读性):

uint32_t x[2];
uint16_t a, b, c, d;
x[0] = ((uint32_t)a << 16) | ((uint32_t)b);
x[1] = ((uint32_t)c << 16) | ((uint32_t)d);

然后我转到了,

x[0] = ((uint32_t)b << 16) | ((uint32_t)a);
x[1] = ((uint32_t)d << 16) | ((uint32_t)c);

哪个是正确的但是在我的小端机器上。然后我想知道为什么当我用某些值测试这些时,字节顺序并不适用。

是否有一种编译器友好的方式来重写库代码以使其与endian无关?

编辑:在第73行,您可以看到NVM_MEMORY的定义,因此大小至少是一致的。

#define NVM_MEMORY        ((volatile uint16_t *)FLASH_ADDR)

2 个答案:

答案 0 :(得分:1)

这个怎么样?

memcpy(raw_fusebits, NVM_MEMORY + (NVMCTRL_USER / 2), sizeof(raw_fusebits));

假设字段按原样复制,一次16位,原始代码中似乎没有任何字节顺序处理。使用memcpy()可以避免严格别名问题。

答案 1 :(得分:1)

在这种情况下,您可以按照John的建议使用memcpy。但是,如果计算了您需要分配的值,memcpy将是不够的。在这种情况下,编译器友好的方法来为C支持的别名值使用union

union {
  struct {
    uint32_t val[2];
  } u32;
  struct {
    uint16_t val[4];
  } u16;
} raw;
...
raw.u16.val[0] = NVM_MEMORY[NVMCTRL_USER / 2];
raw.u16.val[1] = NVM_MEMORY[(NVMCTRL_USER / 2) + 1];
raw.u16.val[2] = NVM_MEMORY[(NVMCTRL_USER / 2) + 2];
raw.u16.val[3] = NVM_MEMORY[(NVMCTRL_USER / 2) + 3];

/* obtain 32-bit values from raw.u32.val[0] and raw.u32.val[1] */