【发布时间】:2026-02-20 06:45:02
【问题描述】:
通常在嵌入式编程中(但不限于)需要序列化一些任意的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 是 uint32_t* 类型的指针,但是它可能指向 uint8_t 字段)。据我了解,这又违反了规则。
可能的解决方案?
用所需类型的数组将有问题的结构包装在一个联合中:
union
{
struct_type s;
uint32_t array[sizeof(struct_type) / 4];
} u;
并将u.array 传递给库函数。
这是正确的方法吗?这是唯一正确的方法吗?还有什么其他方法?
【问题讨论】:
-
非常简单的解决方案:
write_to_eeprom(char const* data, size_t len_times_four). -
@KerrekSB 正如我所说,
write_to_eeprom是一个库函数,这是我们无法控制的。而且,将float*转换为char*是违规行为,不是吗?。 -
@KerrekSB 其实不,不是……任何东西都可以别名为
char*。 -
不,您可以将任何对象解释为字符序列。这明确不是别名违规。
-
是的,您可以将对象中的字节复制到
uint32_的数组中,然后复制整数。 (或者您可能根本不在乎,只是将您的代码限制在您的一个平台上。)
标签: c strict-aliasing