通常在嵌入式编程中(但不限于),需要序列化一些任意struct
,以便通过某个通信通道发送它或写入某个内存。
示例
让我们考虑在N
对齐的内存区域中由不同数据类型组成的结构:
struct
{
float a;
uint8_t b;
uint32_t c;
} s;
现在让我们假设我们有一个库函数
void write_to_eeprom(uint32_t *data, uint32_t len);
将指向数据的指针写为uint32_t*
。现在我们想使用此函数将s
写入eeprom。一种天真的方法是做类似
write_to_eeprom((uint32_t*)&s, sizeof(s)/4);
但这显然违反了严格的别名规则。
第二个例子
struct
{
uint32_t a;
uint8_t b;
uint32_t c;
} s;
在这种情况下,别名(uint32_t*)&s
不违反规则,因为指针与指向第一个字段类型的指针兼容,这是合法的。但!可以实现库函数,使得它正在执行一些指针算法来迭代输入数据,而这个算术结果指针与它们指向的数据不兼容(例如data+1
是{{1}类型的指针但是它可能指向uint8_t字段)。正如我所理解的那样,这再次违反了规则。
可能的解决方案?
将有问题的结构包装在一个带有所需类型数组的联合中:
uint32_t*
并将union
{
struct_type s;
uint32_t array[sizeof(struct_type) / 4];
} u;
传递给库函数。
这是正确的方法吗?这是唯一正确的方法吗?还有什么其他办法?
答案 0 :(得分:2)
只是一个注释我不完全确定,但将uint8_t*
投射到char*
(here)并不总是安全的。
无论你的写函数的最后一个参数想要什么,要写的字节数还是uint32_t
个元素的数量是多少?我们稍后假设,并假设您要编写结构的每个成员以分隔整数。你可以这样做:
uint32_t dest[4] = {0};
memcpy(buffer, &s.a, sizeof(float));
memcpy(buffer+1, &s.b, sizeof(uint8_t));
memcpy(buffer+2, &s.c, sizeof(uint32_t));
write_to_eeprom(buffer, 3 /* Nr of elements */);
如果要将结构元素连续复制到整数数组 - 可以先将结构成员连续复制到字节数组 - 然后将字节数组复制到uint32_t
数组。并且还将字节数作为最后一个参数传递 - sizeof(float)+sizeof(uint8_t)+sizeof(uint32_t)
答案 1 :(得分:0)
考虑到写入eeprom通常比写入普通内存更慢,有时慢得多,使用中间缓冲区很少会影响性能。我意识到这与this comment相反,但我觉得值得考虑,因为它处理所有其他C问题
编写一个具有无对齐,别名或大小问题
的辅助函数extern void write_to_eeprom(/* I'd expect const */ uint32_t *data, uint32_t len);
// Adjust N per system needs
#define BYTES_TO_EEPROM_N 16
void write_bytes_to_eeprom(const void *ptr, size_t size) {
const unsigned char *byte_ptr = ptr;
union {
uint32_t data32[BYTES_TO_EEPROM_N / sizeof (uint32_t)];
unsigned char data8[BYTES_TO_EEPROM_N];
} u;
while (size >= BYTES_TO_EEPROM_N) {
memcpy(u.data8, byte_ptr, BYTES_TO_EEPROM_N); // **
byte_ptr += BYTES_TO_EEPROM_N;
write_to_eeprom(u.data32, BYTES_TO_EEPROM_N / sizeof (uint32_t));
size -= BYTES_TO_EEPROM_N;
}
if (size > 0) {
memcpy(u.data8, byte_ptr, size);
while (size % sizeof (uint32_t)) {
u.data8[size++] = 0; // zero fill
}
write_to_eeprom(u.data32, (uint32_t) size);
}
}
// usage - very simple
write_bytes_to_eeprom(&s, sizeof s);
**可以使用memcpy(u.data32, byte_ptr, BYTES_TO_EEPROM_N);
来处理@zwol issue。