【问题标题】:Creating a buffer for unknown (sized) structs in C在 C 中为未知(大小)结构创建缓冲区
【发布时间】:2016-04-26 21:02:41
【问题描述】:

我目前正在为 RF24Mesh 微控制器层编写一个抽象层来实现消息签名。

因为我想让它尽可能通用,所以我很乐意为 ex 传递任何结构/有效负载:(这只是一个应该能够传递给函数的结构的示例)

  struct payload_sensor {
        uint8_t some_other_sensor_data[32];
        int sensor_id;
        int sensor_data;
  };

到签名函数,然后让它处理存储有效负载/结构,直到完成所有其他操作以发送它。

但事情是这样的,我无法预测传递给函数的结构的确切大小(它应该处理传递给它的任何结构),相同的结构可能会被重用(填充其他数据)并传递给再次使用该功能。我完全想不出一个好的解决方案。

现在如何实现一个适当的缓冲区来存储这些未知的有效负载/结构,直到它们准备好发送?我还必须记住,代码是在 AVR 上运行的,因此 AVR 内存(碎片)可能会成为一个问题。

我对结构和更高级的东西有点陌生,所以请不要介意我遗漏了一些明显的东西。

【问题讨论】:

  • 如果它是未知的,那么为什么它需要是一个结构?传递一个char*void* 和一个大小。
  • 作为 TLV(类型、长度、值)发送。当双方同意T 的定义时,您甚至不需要长度。
  • 传递一个带有 whatever 结构 memcpyed 的缓冲区。按照惯例,让第一个 struct byte/short/int 持有一个标签,告诉另一端即将出现什么类型。

标签: c struct


【解决方案1】:

这类问题通常可以通过定义结构来解决,例如:

struct payload_sensor {
    unsigned int struct_size;
    int sensor_id;
    type any_other_always_present_data;
    char variable_data[];
};

variable_data 是有效载荷的未知部分。

当您知道实际的 variable_data 需要多大时,您可以执行以下操作:

unsigned int actual_length = length_of_variable_data + sizeof(struct payload_sensor);
struct payload_sensor *myStruct = malloc(actual_length);
myStruct->struct_size = actual_length;

并将您的可变数据复制到您想要的结构中:

memcpy(&myStruct->variable_data, &data_source, length_of_variable_data);

基于 cmets 和编辑,听起来您的意图是能够将数据传递给某个必须保留数据副本直到稍后某个时间点的函数,并且该函数不知道它的内容被要求持有。如果是这种情况,您需要做的就是告诉函数每次调用的长度。例如void myFunction(const void *buffer, unsigned int length);:

myFunction(&myStructOfType1, sizeof(myStructOfType1));
myFunction(&myStructOfType2, sizeof(myStructOfType2));

并且,基于您对重用结构的担忧,将myFunction() 复制length 字节从buffer 开始到它存储东西的任何地方(以及记录它存储在副本地址的长度)未来的处理。

【讨论】:

  • 所以基本上我仍然必须将输入限制为某个结构(可以更改哪些数据),以便在发送之前将其存储在其他地方?
  • 您将输入限制为 begin with 某个结构,但该结构中的 struct_size 变量在那里,因此您可以超越结构并跟踪如何你已经超越了很多数据。如果您希望将固定和可变组件保存在内存中的一个位置(例如,能够一次传输所有数据),这是必要的。如果你有明确定义的变量数据集,你应该考虑在你的结构中使用union
  • 虽然我认为使用灵活的数组成员而不是零元素的数组更正确和可读(我不确定它是否是标准的)。
  • @Avamander 我建议您使用 malloc() 来分配您的复制空间。您仍然需要能够存储元数据:指向每个缓冲区的指针和每个缓冲区的长度。也许是struct meta { void *buffer; unsigned int length; struct meta *next; }static struct meta *stored_buffers; 的链接列表,可以找到第一个。不过,更多细节对于堆栈溢出来说太啰嗦了。
  • 在出现“too many cmets”链接之前,我不确定如何开始聊天。如果您的 malloc 实现不处理对齐问题,您可以通过根据需要四舍五入分配的大小并根据您获得的块存储到对齐的偏移量来自己解决。至于碎片问题 - 你通常会如何解决这个问题?如果您没有针对它的平台 API,您可能需要编写自己的(相对简单的)分配器,该分配器在您为问题留出的大量内存上运行。
【解决方案2】:

在 C 中没有像“未知大小的结构”这样的东西。sizeof(payload_sensor) 每次都会有相同的大小。

【讨论】:

  • 该结构的大小确实相同,但是当有人将另一个结构传递给我不知道的函数时?
  • 不可能传递其他类型的对象,除非someone 将通过(payload_sensor *) pointer-to-wrong-object 转换它。但这是一个未定义的行为,你可以不关心这种情况,因为即使是编译器也不会:)
  • payload_sensor 只是传递给函数的结构示例。
【解决方案3】:

您可以将 char[]buffer 传递给具有 whatever 结构 memcpyed 的函数。这样就摆脱了基于结构的函数签名问题。

让你按照惯例传递的所有结构的第一个结构字节/短/整数持有一个标签,告诉另一端什么类型来了。

如果消息是通过线路(例如 UART)发送的,请注意平台之间的字节顺序和不同的sizeof() 问题,这可能会使您的协议基于结构的尝试成为风向标。

如果您正在交换跨平台消息(例如在 PC 和 AVR 之间),您真的不想处理编译器不兼容问题。在这种情况下,definedocument 用于线路的协议不基于 C 结构,并将它们所属的字节手动放入 unsigned char 缓冲区。或者使用基于 ASCII 的协议。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-26
    • 2017-04-07
    • 2018-05-24
    • 1970-01-01
    • 1970-01-01
    • 2012-06-07
    相关资源
    最近更新 更多