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标准的偏差,并且只在这个特定的地方使用这种偏差,或者是否有更好的解决方案来解决这个问题?
答案 0 :(得分:5)
你的日志联合正是你应该被允许使用的那种联合,它正在做的是数据打包,MISRA明确指出是可接受的偏差,所以偏差是你应该做的。几乎所有使用MISRA的人都以这种方式偏离了这条规则。 (这是一个相当糟糕的规则,它看起来会被降级为咨询或在下一个MISRA版本中完全删除。)
但你需要记录:
要避免填充/对齐问题,您可以编写如下内容:
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*);