6

米斯拉说要禁止所有工会。我也知道,只要彻底讨论和记录偏差是允许的。

我们有一个微控制器和一个外部 eeprom 来存储统计数据(事件/错误记录、参数设置等等)。

事件日志由大约 80 多个事件计数器组成,其中一些是 8、16 和 32 位(全部无符号)。参数存储由大约 200 个参数组成,还混合有 8、16 和 32 位值(无符号)。

我们正在重写所有代码以符合 MISRA,并且这些值之前定义如下:

typedef struct
{
  U16BIT eventLogVar1;
  U32BIT eventLogVar2;
  U8BIT  eventLogVar3;
  U8BIT  eventLogVar4;
  U32BIT eventLogVar5;
} EVENT_LOG;

typedef union
{
  EVENT_LOG log;
  U8BIT     array[sizeof(EVENT_LOG)];
} ELOG;

ELOG log;

现在这并不真正符合 MISRA。参数日志也是如此。然而,这是从 eeprom 读取和写入的最简单方法,因为我只需通过数组读取/写入即可从 eeprom 读取/写入。

我们还有一些其他规则是不允许我们违反的。没有全局(外部)变量(通过头文件)。如果需要,所有局部变量只能通过 get/set 函数访问。

这意味着,如果我们需要完全写出所有这些参数,那么这些参数都应该有自己的 get/set 函数,以便在整个应用程序中更改它们。

我考虑过的解决方案如下:

#ifdef EITHER
enum
{
    eventLogVar1 = 0; /* 00 */
    pad01;            /* 01 */
    eventLogVar2;     /* 02 */
    pad03;            /* 03 */
    pad04;            /* 04 */
    pad05;            /* 05 */
    eventLogVar3;     /* 06 */
    eventLogVar4;     /* 07 */
    eventLogVar5;     /* 08 */
    pad09;            /* 09 */
    pad10;            /* 10 */
    pad11;            /* 11 */
}
#else /* OR */
#define eventLogVar1 0 /* 2 bytes */
#define eventLogVar2 2 /* 4 bytes */
#define eventLogVar3 6 /* 1 byte  */
#define eventLogVar4 7 /* 1 byte  */
#define eventLogVar5 8 /* 4 bytes */
#endif
#define eventLogLastLength 4

U8BIT eventLog[eventLogVar5 + eventLogLastLength];

U8BIT getU8BIT(U8BIT index){}
U16BIT getU16BIT(U8BIT index){}
U32BIT getU32BIT(U8BIT index){}

void setU8BIT(U8BIT index, U8BIT val){}  
void setU16BIT(U8BIT index, U16BIT val){}
void setU32BIT(U8BIT index, U32BIT val){}

但是,如果添加或删除值,这会带来繁琐的重构。这也意味着不能使用数组类型的值(并且有一些),如果使用或多或少的某种类型(例如传感器),可以通过定义长度来改变这些值。


你对这个具体问题有什么看法。在这种特定情况下,我/我们是否会更好地记录我们与 MISRA 标准的偏差,并且只在这个特定的地方使用这种偏差,还是有更好的解决方案来解决这个问题?

4

3 回答 3

6

您的日志联合正是您应该被允许使用的那种联合,它正在做的是数据打包,这是 MISRA 明确指出的可接受偏差,所以偏差是您应该做的。几乎所有使用 MISRA 的人都以这种方式偏离了这条规则。(这是一个相当糟糕的规则,看起来它将在下一个 MISRA 版本中被降级为建议或完全删除。)

但您需要记录:

  • 填充字节和对齐是否会成为问题。
  • 如果代码需要可移植,是否字节序可能是一个问题。

要避免填充/对齐问题,您可以编写如下内容:

COMPILE_TIME_ASSERT( (sizeof(union_member1)+sizeof(union_member2)+...) ==
                     sizeof(union_type) );

其中 COMPILE_TIME_ASSERT 是一些宏,如果未传递正值,则会产生编译器错误。这确保不存在结构/联合填充。


进一步评论:

enum 是一个糟糕的解决方案,因为它本身有许多缺陷:枚举类型的变量具有实现定义的大小,而枚举常量具有带符号的 int 类型。这将与关于隐式类型转换的 MISRA 规则发生冲突,您将被迫添加大量类型转换。

如果需要,所有局部变量只能通过 get/set 函数访问。

还需要声明它们static以缩小其范围。我从你的片段中注意到你没有这样做。static由 MISRA:2004 8.11 强制执行。

于 2012-08-10T12:00:46.723 回答
5

如果使用得当,联合是一个非常有用的结构。

当人们试图用它们做聪明的事情时,它们也会有未定义的行为。这是为了防止 MISRA 指南试图防止的未定义行为。但即使是规则(MISRA C:2004 中的 18.2)也给出了工会有用的情况。

您给出的示例就是这样一种有用的情况,并且正是 MISRA 偏差程序适用的那种情况。


免责声明:我是 MISRA C 工作组的成员,但我以个人身份发帖。我的观点不应被视为MISRA的官方政策。

于 2012-11-08T07:32:34.363 回答
2

MISRA 对 memcpy 有什么看法?为什么不使用联合,而不是只使用 EVENT_LOG?当您向/从 EEPROM 序列化时,请使用如下临时数组:

EVENT_LOG log;

U8BIT array[sizeof(EVENT_LOG)];
// populate array

memcpy(&log, array, sizeof(EVENT_LOG));

// do similar thing when writing to eeprom

union 将数据和序列化格式混合在一起,函数可能更适合哪里?

void EVENT_LOG_write_to_eeprom(const EVENT_LOG*);
void EVENT_LOG_read_from_eeprom(EVENT_LOG*);
于 2012-08-09T12:23:36.297 回答