关于MISRA的关于C的工会的想法

时间:2012-08-09 11:57:45

标签: c misra

Misra说禁止所有工会。我也知道,只要对它们进行彻底的讨论和记录,就允许偏差。

我们有一个微控制器和一个外部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标准的偏差,并且只在这个特定的地方使用这种偏差,或者是否有更好的解决方案来解决这个问题?

3 个答案:

答案 0 :(得分:5)

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

但你需要记录:

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

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

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

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


进一步评论:

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

  

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

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

答案 1 :(得分:4)

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

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

您提供的示例是一个有用情况,并且正是MISRA偏离程序所针对的情况。


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

答案 2 :(得分:1)

MISRA对memcpy的评价是什么?为什么不使用EVENT_LOG而不是联盟呢?当您序列化到EEPROM或从EEPROM序列化使用这样的临时数组:

EVENT_LOG log;

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

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

// do similar thing when writing to eeprom

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

void EVENT_LOG_write_to_eeprom(const EVENT_LOG*);
void EVENT_LOG_read_from_eeprom(EVENT_LOG*);