简体   繁体   English

MISRA-C:将 char 数组转换为结构指针是否安全?

[英]MISRA-C: Is it safe to cast a char array to a structure pointer?

When I have a user defined type like the following:当我有如下用户定义的类型时:

typedef struct MyData_t {
  uint16_t val;
  ...
} MyData;

And a simple array that I want to use to store different types of structures in:还有一个简单的数组,我想用它来存储不同类型的结构:

uint8_t buffer[];

And I then want to create a structure pointer that uses the array to store the data of that structure:然后我想创建一个结构指针,它使用数组来存储该结构的数据:

MyData* freelist = (MyData*) buffer;

Then I get the MISRA 2012 Error:然后我得到 MISRA 2012 错误:

Note 9087: cast performed between a pointer to object type and a pointer to a different object type [MISRA 2012 Rule 11.3, required]注释 9087:在指向对象类型的指针和指向不同对象类型的指针之间执行转换 [MISRA 2012 Rule 11.3,必需]

This rule says because of possible alignment issues it is never safe to cast pointers between different types of objects.这条规则说由于可能的对齐问题,在不同类型的对象之间转换指针是不安全的。 My question is: How common is it in an embedded environment for compilers to cause any issues in this case?我的问题是:在嵌入式环境中,编译器在这种情况下引起任何问题的常见程度如何? And how could I prevent these issues when forced to keep the implementation concept (about the buffer array that stores different types of objects)?当被迫保留实现概念(关于存储不同类型对象的缓冲区数组)时,我该如何防止这些问题?

If you dereference freelist then you invoke undefined behavior.如果您取消引用freelist ,那么您将调用未定义的行为。 Both because of possible alignment issues as well as strict aliasing.两者都是因为可能的对齐问题以及严格的混叠。 It's a bug, MISRA or no MISRA.这是一个错误,MISRA 或没有 MISRA。 The easiest fix is to use memcpy instead.最简单的解决方法是改用memcpy

How common is it in an embedded environment for compilers to cause any issues in this case?在这种情况下,编译器在嵌入式环境中引起任何问题的情况有多普遍?

In case of alignment, it depends on the hardware.在对齐的情况下,它取决于硬件。 Some architectures like MIPS are very picky with alignment, others like generic 8-bitter MCUs couldn't care less.像 MIPS 这样的架构对对齐非常挑剔,而像通用 8 位 MCU 等其他架构则不在乎。

As for strict aliasing bugs, it was common for the gcc compiler to go haywire upon strict aliasing violations at the time it started to gain popularity in embedded systems, somewhere around the beginning of the ARM hype year 2008-2010 somewhere (gcc versions around 3.x something?).至于严格别名错误,当 gcc 编译器开始在嵌入式系统中流行时,在 2008-2010 年 ARM 炒作年开始时(gcc 版本大约 3 .x 什么东西?)。 Modern gcc versions have less strict aliasing mis-optimizations.现代 gcc 版本的别名错误优化不那么严格。 Still always compile with -fno-strict-aliasing when using gcc, since the compiler is instable and generally dangerous to use when strict aliasing is allowed.使用 gcc 时仍然始终使用-fno-strict-aliasing进行编译,因为编译器不稳定并且在允许使用严格别名时使用通常很危险。

As for the regular embedded systems compilers, they are usually not as stupid as to abuse strict aliasing optimizations since they want to sell their compilers.至于常规的嵌入式系统编译器,他们通常不会愚蠢到滥用严格的别名优化,因为他们想出售他们的编译器。


Notably, the other way around - going from struct to character pointer - is fine.值得注意的是,反过来——从结构到字符指针——也很好。 MISRA C:2012 11.3 then lists the following explicit exception: MISRA C:2012 11.3 然后列出以下显式异常:

Exception例外
It is permitted to convert a pointer to object type into a pointer to one of the object types char , signed char or unsigned char .允许将指向对象类型的指针转​​换为指向对象类型charsigned charunsigned char之一的指针。


EDIT编辑

If it's ok to break a few advisory MISRA rules like casting between integers and pointers, then perhaps something like the example below could be an option.如果可以打破一些建议性 MISRA 规则,例如在整数和指针之间进行强制转换,那么可能类似于下面的示例可能是一种选择。 No pointer conversions, no pointer arithmetic, no strict aliasing problems.没有指针转换,没有指针运算,没有严格的别名问题。 You'll have to cast the integer into a struct pointer type on the caller side, which violates advisory rules.您必须在调用方将整数转换为结构指针类型,这违反了咨询规则。 You have to set aside an aligned chunk of data at the address of mempool_addr with size mempool_maxsize in your linker script.您必须在链接描述文件中在mempool_addr的地址留出一个对齐的数据块,大小为mempool_maxsize

#include <stdint.h>
#include <stddef.h>

#define mempool_maxsize 1024u
#define mempool_addr 0x10000u

static size_t mempool_size=0u;

uintptr_t static_alloc (size_t size)
{
  uintptr_t result;

  if(mempool_size + size > mempool_maxsize)
  {
    return 0;
  }

  if((size % _Alignof(int)) != 0)
  {
    size += _Alignof(int) - (size % _Alignof(int));
  }

  result = mempool_addr + mempool_size;
  mempool_size += size;
  return result;
}

How common is it in an embedded environment for compilers to cause any issues in this case?在这种情况下,编译器在嵌入式环境中引起任何问题的情况有多普遍?

Common enough as it fails to meet alignment needs.足够常见,因为它无法满足对齐需求。 E. g.例如。 buffer[] may exist on an odd address and access to uint16_t needs an even one. buffer[]可能存在于奇数地址上,访问uint16_t需要偶数地址。 Result: bus violation.结果:总线违规。 Any casting will not help.任何铸造都无济于事。

how could I prevent these issues我怎样才能防止这些问题

Use a union of uint8_t[] and struct MyData_t to align and avoid aliasing issues too.使用uint8_t[]struct MyData_tunion来对齐并避免别名问题。


Various ways to insure uint8_t buffer[] is aligned well.确保uint8_t buffer[]对齐良好的各种方法。 Example:例子:

#include <stddef.h>
#define BUF_N 100 

union {
  uint8_t buffer[BUF_N];
  max_align_t a; // Or any wide type like complex long double
} u;

And use u.buffer instead of buffer .并使用u.buffer而不是buffer

Also research _Alignas还研究_Alignas

The rationale for this Rule is actually the same for restricting the use of union s - there are many pitfalls for the unwary.这条规则的基本原理实际上与限制union的使用相同——粗心的人有很多陷阱。

  • Alignment, as discussed, is probably the primary issue正如所讨论的,对齐可能是主要问题
  • Strict aliasing, again as mentioned严格的别名,再次如前所述
  • Padding within the struct ure, is another struct内的填充是另一个

If you take appropriate steps to (a) ensure alignment and (b) ensure the packing/unpacking in to and out of the structure is correct, and (c) ensuring you do not violate the strict aliasing considerations, you could probably get away with it.如果您采取适当的步骤来 (a) 确保对齐和 (b) 确保进出结构的打包/解包是正确的,以及 (c) 确保您不违反严格的混叠注意事项,您可能会侥幸逃脱它。

You could, of course, disapply R.18.1 and use a union of myData_t myData and uint8_t data[] ...当然,您可以不应用 R.18.1 并使用union myData_t myDatauint8_t data[]的联合...

But frankly, you'd probably be better off explicitly unpacking the data field by field.但坦率地说,您最好逐个字段地显式解包数据。

See profile for affiliations查看隶属关系的个人资料

You can probably circumvent the error through first casting it to a void* pointer.您可以通过首先将其转换为void*指针来规避该错误。

That being said, it is still implementation defined behaviour and therefore goes against MISRA guidelines.话虽如此,它仍然是实现定义的行为,因此违反了 MISRA 指南。 You are only guaranteed validity as per the standard if you cast to void* and back to the exact same type.如果您转换为void*并返回完全相同的类型,则只有按照标准保证有效性。

However, there may often be cases in embedded systems where this is needed, eg.然而,在嵌入式系统中可能经常存在需要这样做的情况,例如。 to access specific memory areas.访问特定的内存区域。 I have had some at least.我至少吃过一些。 In these cases you'd need to have this use signed-off by management as per MISRA.在这些情况下,您需要根据 MISRA 由管理层签署此用途。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM