【问题标题】:Overlay struct to arbitary buffer将结构覆盖到任意缓冲区
【发布时间】:2021-03-17 00:43:16
【问题描述】:

我是一名“新”C 程序员,但也是一名老汇编程序员,几天来一直在寻找答案。

我正在尝试使用 C 结构构造解析消息中的多个字段(这是一个带有嵌入式 RTU modbus 数据包的 LORA 无线电)。

我有这个显示我的问题的示例代码:

#include <stdio.h>
#include <stdint.h>

struct MessageTable{
    uint8_t msg_id;
    uint8_t from;
    uint8_t to;
    unsigned flags1 : 1;
    unsigned retransmitted : 1;
    unsigned hops : 4;
    union {
        unsigned long millisecs;
        unsigned char bytes[sizeof(unsigned long)];
    } ms;
};


struct MessageTable message, *mp;
struct MessageTable message_table[8] = {0};
char buf[256];

void main(void) {
    int i;
    for (i=0; i<255; i++)
        buf[i] = i;

    mp = (struct MessageTable) &buf;
    printf("To: %u, From: %u", mp->to, mp->from);
}

当我尝试编译时,我得到:

question.c: In function ‘main’:
question.c:27:18: error: conversion to non-scalar type requested
   27 |     mp = (struct MessageTable) &buf;
      |                  ^~~~~~~~~~~~

我正在尝试做的是,在缓冲区空间中的某个任意位置覆盖结构,以便命名访问不同的字段,而不是使用硬编码的偏移量(即 to=buf[2];retransmitted = buf[3]&amp;02x;

什么是干净、可读、合适的方法?

注意:在不同的 buf 位置会有多个结构(LORA 路由、Modbus Send、Modbus Rx、Modbus err 等...) 而且,这是纯 C,而不是 C++。

我不在乎缓冲区是否“跑出”结构的末尾,代码结构会处理这个问题。

【问题讨论】:

标签: c data-structures struct


【解决方案1】:

默认情况下,C 结构字段不保证彼此直接相邻,此外,位字段可以重新排序。允许实现对位域重新排序并实现填充,以便有效地满足系统内存对齐要求。如果您需要保证 struct 字段在内存中彼此紧邻(没有填充)并按照您指定的顺序放置,您需要查看如何告诉编译器创建打包结构。这不是标准的 C 语言(但有必要确保您尝试完成的工作能够正常工作——它可能(但不能保证)以其他方式工作),并且每个编译器都有自己的方式。

【讨论】:

【解决方案2】:

你有两个问题:

mp = (struct MessageTable) &buf;

第一个是buf由于数组/指针转换已经是一个指针。 C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)

第二个问题是您正在转换为struct MessageTable 而不是指向 struct MessageTable 的指针。您可以通过以下方式更正两者:

    mp = (struct MessageTable*) buf;

此外,除非您在 独立环境 中编程(没有任何操作系统的好处),在符合标准的实现中,main 的允许声明为 int main (void) 和 @987654332 @(您将看到用等效的 char **argv 编写)。请参阅:C11 Standard - §5.1.2.2.1 Program startup(p1)。另请参阅:What should main() return in C and C++? 在独立环境中,程序启动时调用的函数的名称和类型是实现定义的。见:C11 Standard - 5.1.2.1 Freestanding environment

总而言之,您将拥有:

#include <stdio.h>
#include <stdint.h>

struct MessageTable{
    uint8_t msg_id;
    uint8_t from;
    uint8_t to;
    unsigned flags1 : 1;
    unsigned retransmitted : 1;
    unsigned hops : 4;
    union {
        unsigned long millisecs;
        unsigned char bytes[sizeof(unsigned long)];
    } ms;
};


struct MessageTable message, *mp;
struct MessageTable message_table[8] = {0};
char buf[256];

int main(void) {
    int i;
    for (i=0; i<255; i++)
        buf[i] = i;

    mp = (struct MessageTable*) buf;
    printf("To: %u, From: %u", mp->to, mp->from);
}

使用/输出示例

$ ./bin/struct_buf_overlay
To: 2, From: 1

【讨论】:

  • 是的,这是一个“独立环境”,它自己在 Atmel tiny8 上运行。
  • 啊哈!这更有意义。有趣的小板。我有几个有趣的旧 TI 板。
【解决方案3】:

首先解决您在此行的错误消息:

mp = (struct MessageTable) &buf;

在这里,您尝试将具有 char (*)[256] 类型(即指向数组的指针)的 &amp;buf 转换为不是指针类型的 struct MessageTable。在大多数情况下,数组会衰减为指向第一个元素的指针,因此您不需要获取其地址,并且需要将其转换为指针类型:

mp = (struct MessageTable *)buf;

然而,另一个问题是:

  • 结构可能不是您期望的大小
  • bitfieds 的顺序可能不是您所期望的
  • 如果缓冲区未正确对齐结构中的字段,您可能会产生错误。

【讨论】:

  • 是的,我明白,它在 Atmel Tiny 上运行(但我正在 Linux 上进行测试),所以我看到了对齐问题。到目前为止,我在 Tiny with GCC 上还没有看到这一点,我可以解释这一点,
  • 如果我可以通过名称访问它们,我不关心位的物理位置。 (我正在写两端)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-22
  • 2014-12-28
相关资源
最近更新 更多