C 将结构成员写入内存的通用接口模式?

C 将结构成员写入内存的通用接口模式?,c,memory,struct,embedded,C,Memory,Struct,Embedded,我正在使用一个具有一些外部非易失性内存的嵌入式设备(无操作系统) 我在NV内存中存储单个结构数据的多个副本。我读写内存中的数据,作为整体结构和作为单个成员 目前我有一个工作实现。当处理内存操作的模块被初始化时,它会得到RAM中结构的位置,并作为其上下文的一部分进行存储。这意味着当读写单个成员时,它可以通过从成员的位置减去前面提到的位置来计算成员的偏移量。然后,它使用该偏移量来知道在NV内存中的何处找到副本 e、 g.在psuedo代码中: init: wm_ram = pointer t

我正在使用一个具有一些外部非易失性内存的嵌入式设备(无操作系统)

我在NV内存中存储单个结构数据的多个副本。我读写内存中的数据,作为整体结构和作为单个成员

目前我有一个工作实现。当处理内存操作的模块被初始化时,它会得到RAM中结构的位置,并作为其上下文的一部分进行存储。这意味着当读写单个成员时,它可以通过从成员的位置减去前面提到的位置来计算成员的偏移量。然后,它使用该偏移量来知道在NV内存中的何处找到副本

e、 g.在psuedo代码中:

init:
    wm_ram = pointer to working memory struct start in RAM;

write to a member from the member in RAM:
    offset = working memory member location in RAM - wm_ram;
    sector address = address of selected copy of struct in NV to write to;
    write data from working memory member location to NV sector @ start + 
        offset + sector address;
(struct类型有几个嵌套结构,因此我认为offsetof()在这里)

这就为常见的操作带来了非常简单和熟悉的感觉:

int write_or_read(*handle, copyNumber, *data, size);
这很好,直到我还想从RAM存储第二个结构。这意味着现在RAM中有两个地址,内存模块需要跟踪以正确计算偏移量,再加上一些额外的决策工作以决定使用哪个地址

因此,我推断struct RAM开始位置应该在内存操作时传入,而不是作为内存模块上下文的一部分保存

但这开始变得有点笨拙:

int write_or_read(*handle, copyNumber, *data, *data_struct_start, size);
所以我的问题是:是否有任何惯例或模式经常或常用于类似或类似的情况,希望也能产生简单和熟悉的操作函数签名

编辑:进一步解释 NV内存是一个简单的外部设备,类似于EEPROM,具有2KB的内存。“我的结构”包含多种数据类型,包括其他结构,每个结构的大小总计约为600B。设备没有缓冲;它实际上是FRAM,所以速度相当快,但是我的开销很少,所以我不想只为一个成员编写整个结构

图中显示我目前在RAM中有一个结构,在NV内存中有多个副本。如果我想读或写特定副本中的特定成员,那么我只需获取成员的地址(
&myStruct->member_2
),就可以在RAM中获得该成员的地址,但在NV中应该使用什么地址?RAM地址与NV中的地址不一致(例如RAM中的地址0x300!=NV中的0x300),因此我需要其他方法来确定这一点

如果读或写函数被告知将
myStruct
member_2
的副本写入NV中的struct_1,那么它需要写入哪个字节地址


也许我试图保持界面如此简单是错误的?

不要使用结构进行序列化

相反,请在明文数据文件(CSV、JSON、XML等)中定义设置变量:

然后使用您选择的脚本语言将该数据文件转换为源文件。您可以根据类型确定所需的大小,并自动计算表中的NV内存偏移量

脚本弹出的其中一个文件是ID的列表:

typedef enum {
    ID_Foo,
    ID_Bar,
    ID_COUNT
} ID_T;
您提供的API非常简单:

int32_t ReadInt32(ID_T id);
float ReadFloat(ID_T id);
void WriteInt32(ID_T id, int32_t value);
void WriteFloat(ID_T id, float value);
这有几个好处:

  • 偏移量和地址的所有脏细节都对其他代码隐藏
  • 现在您可以轻松地扩展它,例如添加min/max/description字段
  • 您可以通过更改脚本轻松地更改数据表的格式,而API保持不变
  • 如果您以后需要其他功能,如通过串行端口读取/写入设置的能力,则设置将列在一个位置
  • 如果为指向数据的指针添加额外字段,则可以在调用写函数时自动更新实际RAM值
  • 您可以创建其他脚本来生成设置的文档(pdf/doc/etc)

读这篇文章我有点困惑,所以我可能误解了什么。首先,为什么要处理偏移?如果它只在RAM中(请参见
memcpy(*dest,*src,size)
),就永远不会这样做。一种简单的方法是在init上复制到RAM,然后在change/where上提交到NV。提交时,请将其视为字节数组(但请注意此处的填充问题)或解析结构。了解您的存储是否足够大以容纳完整的文件系统(如所需的原始文件系统),或者是否只有512字节或类似的EEPROM,这将非常有价值。另外,如果您的RAM允许缓冲整个结构。将数据保留为分层数据库而不进行进一步格式化将非常混乱和不稳定。@domen添加了进一步的解释-有帮助吗?@Vroomfondel添加的信息有帮助吗?如果您只是简单地复制结构,请使用memcpy之类的工具,忽略不变的字节。另一种方法是从
foo->bar
复制到nvram位置
struct\u offset+offset of(struct ss,bar)
,大小
sizeof(foo->bar)
。对于多个嵌套结构,您可能需要使用多个
offsetof()
,或类似
(size\t)和((struct ss*)NULL)->bar等难看的工具,以获得一个好的书面答案。但是,如果我有一个PC类型的环境和相关的空间,我一开始就不会走这条路。存储所有额外的ASCII编码的内存需求,加上解析和编码的明文数据所需的代码和处理时间,使得这对于嵌入式环境来说是一个立即无法启动的过程(我甚至没有文件系统!)。@Toby No,重点是您使用脚本生成源代码文件,并将它们包含在你的构建中。啊,对不起,我第一次误读了-interesting@Toby我想补充一点,我走过了这条路:首先使用结构,然后在c源文件中手写表格,最后编写脚本生成表格。在印度
int32_t ReadInt32(ID_T id);
float ReadFloat(ID_T id);
void WriteInt32(ID_T id, int32_t value);
void WriteFloat(ID_T id, float value);