C&C++   发布时间:2022-04-03  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了使用强制转换来访问像结构一样的字节数组?大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
@H_801_1@
@H_607_4@ 我正在研究基于微控制器的软件项目.
该项目的一部分是二进制协议的解析器.
协议是固定的,不能更改.
PC充当“主设备”并主要发送命令,这些命令必须由“从设备”即微控制器板执行.

协议数据由硬件通信接口接收,例如,UART,CAN或以太网.
那不是问题.

在接收到帧的所有字节(4-10,取决于命令)之后,将它们存储在“uint8_t cmdBuffer [10]”类型的缓冲区中并设置标志,表示现在可以执行该命令.
帧的第一个字节(cmdBuffer [0])包含命令,帧的其余部分是命令的参数,根据命令的不同,数字和大小可能不同.
这意味着,有效载荷可以通过多种方式进行解释.对于每个可能的命令,数据字节都会改变它们的含义.

我不希望有太多丑陋的操作,而是自我记录代码.
所以我的方法是:

>我为每个命令创建一个“typedef struct”
>确定命令后,指向cmdBuffer的指针将转换为我的新typedef的指针
>通过这样做,我可以访问命令的参数作为结构成员,避免数组访问中的幻数,参数的位操作> 8位,更容易阅读

例:

typedef struct
{
    uint8_t commandCode;
    uint8_t parameter_1;
    uint32_t anotherParameter;
    uint16 oneMoreParameter;
}payloadA_t;

//typedefs for payloadB_t and payloadC_t,which may have different parameters

void parseProtocolData(uint8_t *data,uint8_t length)
{
  uint8_t commandToExecute;

  //this,in fact,just returns data[0]
  commandToExecute = getPayloadType(data,length);

  if (commandToExecute == COMMAND_A)
  {
    executeCommand_A( (payloadA_t *) data);
  }
  else if (commandToExecute == COMMAND_B)
  {
    executeCommand_B( (payloadB_t *) data);
  }
  else if (commandToExecute == COMMAND_C)
  {
    executeCommand_C( (payloadC_t *) data);
  }
  else
  {
    //error,unkNown command
  }
}@H_874_27@ 
 

我看到两个问题:

>首先,根据微控制器架构,byteorder可以是intel或motorola,用于2或4字节参数.
这应该不是什么大问题.协议本身使用网络字节顺序.在目标控制器上,可以使用宏来更正顺序.
>主要问题:我的tyepdef结构中可能有填充字节.我正在使用gcc,@R_281_9447@在我的typedef中添加一个“packed”属性.其他编译器为此提供了编译指示.但是,在32位机器上,打包结构将导致更大(和更慢)的机器代码.好吧,这也可能不是问题.但是我听说,访问未对齐的内存时可能会出现硬件故障(例如,在ARM体系结构上).

有很多命令(大约50个),所以我不希望将cmdBuffer作为数组访问
我认为与“阵列方法”相比,“结构方法”提高了代码的可读性

所以我的问题:

>这种方法可以,还是只是一个肮脏的黑客?
>是否存在编译器可以依赖“严格别名规则”并使我的方法不起作用的情况?
>有更好的解决方案吗?你会如何解决这个问题?
>这可以保存,至少一点,便携?

问候,
lugge

解决方法

@H_404_54@ 通常,由于填充,结构对于存储数据协议是危险的.对于可移植代码,您可能希望避免使用它们.因此,保持原始数据阵列仍然是最好的主意.您只需要根据收到的命令以不同的方式解释它.

这种情况是需要某种多态性的典型示例.不幸的是,C没有对该OO功能的内置支持,因此您必须自己创建它.

最好的方法取决于这些不同类型数据的性质.由于我不知道,我只能以这种方式提出建议,它可能适用于您的具体情况,也可能不是最佳选择:

typedef enum
{
  COMMAND_THIS,COMMAND_THAT,... // all 50 commands

  COMMANDS_N // a constant which is equal to the number of commands
} cmd_index_t;


typedef struct
{
  uint8_t      command;        // the original command,can be anything
  cmd_index_t  index;          // a number 0 to 49
  uint8_t      data [MAX];     // the raw data
} cmd_t;@H_874_27@ 
 

然后,第一步是在收到命令后,将其转换为相应的索引.

// ...receive data and place it in cmdBuffer[10],then:
cmd_t cmd;
cmd_create(&cmd,cmdBuffer[0],&cmdBuffer[1]);

...

void cmd_create (cmd_t* cmd,uint8_t command,uint8_t* data)
{
   cmd->command = command;
   memcpy(cmd->data,data,MAX);

   switch(command)
   {
     case THIS: cmd->index = COMMAND_THIS; break;
     case THAT: cmd->index = COMMAND_THAT; break;
     ... 
   }
}@H_874_27@ 
 

一旦索引0到N意味着您可以实现查找表.每个这样的查找表可以是函数指针的数组,其确定数据的特定解释.例如:

typedef void (*interpreter_func_t)(uint8_t* data);

const interpreter_func_t interpret [COMMANDS_N] =
{
  &interpret_this_command,&interpret_that_command,...
};@H_874_27@ 
 

使用:

interpret[cmd->index] (cmd->data);@H_874_27@ 
 

然后,您可以为不同的任务创建类似的查找表.

initialize [cmd->index] (cmd->data);
   interpret  [cmd->index] (cmd->data);
   repackage  [cmd->index] (cmd->data);
   do_stuff   [cmd->index] (cmd->data);
   ...@H_874_27@ 
 

对不同的体系结构使用不同的查找表.像endianess这样的东西可以在解释器函数中处理.您当然可以更改函数原型,也许您需要返回一些内容或传递更多参数等.

请注意,上述示例最适合所有命令导致相同类型的操作.如果你需要根据命令做完全不同的事情,其他方法更合适.

大佬总结

以上是大佬教程为你收集整理的使用强制转换来访问像结构一样的字节数组?全部内容,希望文章能够帮你解决使用强制转换来访问像结构一样的字节数组?所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。