c语言 - MISRA 要求函数的单点出口用于"lookup table"函数



Misra标准要求函数只有一个出口点,但我有以下的转换"代码

typedef enum { CASE_A, CASE_B, CASE_C } my_enum_t;
int my_conv_funct(my_enum_t value)
{
switch(value)
{
case CASE_A:
return 0;
case CASE_B:
return 1;
case CASE_C:
return 2;
default:
break;
}
log_error("ERROR!!!!!");
assert(1==0);
}

这个有效吗?我需要把它转换成一个单一的返回函数?处理默认情况的最佳方法是什么?

这在理论上创建了一个不可达的代码(错误是在向枚举中添加值而没有添加相应的大小写时发出警告)

这是一个嵌入式系统有这些断言创建问题?

谢谢,尼克

编辑:

如果没有错误,就不应该调用默认情况(例如,程序员在enum中添加了另一个值,而没有添加相应的情况

)。另一个选项是完全删除默认值,但这违反了另一条misra规则

typedef enum { CASE_A, CASE_B, CASE_C } my_enum_t;
int my_conv_funct(my_enum_t value)
{
switch(value)
{
case CASE_A:
return 0;
case CASE_B:
return 1;
case CASE_C:
return 2;
}
//should never reach this line
assert(1==0);
}

如果我编译并且没有在枚举中指定所有的情况(我认为),这将生成一个警告

非常简单:

int my_conv_funct(my_enum_t value)
{
int result = -1;
switch(value)
{
case CASE_A:
result = 0;
break;
case CASE_B:
result = 1;
break;
case CASE_C:
result = 2;
break;
default:
break;
}
if(result == -1)
{
log_error("ERROR!!!!!");
assert(1==0);
}
return result;
}

这是否有效?

它不符合您所描述的MISRA规则。

我需要将其转换为单个返回函数?

为了遵守MISRA规则,可以。

处理默认情况的最佳方法是什么?

我们无法判断什么是"最好的"。

这是一个嵌入式系统顺便有那些断言创建问题?

断言的思想是帮助您在开发过程中发现编程错误,但是(原则上)它可以通过打算在生产中使用的代码中的构建选项禁用。如果遵循该模型,则断言本身可能不会产生问题,但是在默认情况下(如果禁用断言)函数不返回值的事实会产生问题。如果程序必须在执行默认情况的情况下终止,那么它应该调用abort(),或具有此效果的其他函数。否则,在默认情况下,它应该返回一个合理的值。

我可能会这样写这个函数:

int my_conv_funct(my_enum_t value)
{
switch(value)
{
case CASE_A:
case CASE_B:
case CASE_C:
break;
default:
log_error("ERROR!!!!!");
assert(0);
break;
}
return value;
}

现在函数只有一个退出点,如果它返回,则返回其参数(隐式转换为类型int)。

首先请检查这个答案:计算函数返回值的最佳实践。MISRA-C规则是建议性的,我建议对它进行永久的偏离。我个人将其替换为如下规则:

在一个函数中应该避免使用多个返回语句,除非可以使代码更易于阅读/维护。"

避免在嵌套的复杂代码中从多个位置返回的理由是合理的,但在干净易读的函数中就不那么合理了。

在你的具体情况下,我可能会重写函数像这样(MISRA兼容而不忽略规则):

uint32_t my_conv_funct (my_enum_t value)
{
uint32_t result;
switch(value)
{
case CASE_A: result = 0; break;
case CASE_B: result = 1; break;
case CASE_C: result = 2; break;
default:
{
// error handling here
}
}
return result;
}

或者(偏离规则):

uint32_t my_conv_funct (my_enum_t value)
{
static const uint32_t lut[] = { CASE_A, CASE_B, CASE_C };
for(size_t i=0; i<sizeof lut/sizeof *lut; i++)
{
if(lut[i] == value)
{
return i;
}
}
/* error handling */
return some_error_code;
}

假设项的数量不大,在这种情况下,二分查找可能效率更低。

这又假设枚举常量不对应于0、1和2,在这种情况下,整个函数是无意义的。

MISRA C"单一出口"的理由;规则是因为它是功能安全标准(例如IEC 61508和ISO 26262)的要求。

也是建议性,因此如果情况需要,可以取消

我个人的观点是,一个switch语句很少需要多个出口——很容易在结构上避免它们……然而,在某些情况下(例如参数验证),它可能是有意义的。

,

作为题外话,assert()的使用不符合MISRA C,因为它扩展到abort()违反了规则21.8 -这在嵌入式系统中也是非常不受欢迎的行为(在托管环境中也是有问题的)…

——

更新后的问题现在已经扩展到包括:

如果没有错误(例如程序员在enum中添加了另一个值而没有添加相应的case

),则不应该调用默认情况。另一个选项是完全删除默认值,但这违反了另一条misra规则

我不同意…

默认值是作为错误捕获机制存在的-特别是在实时/嵌入式系统中,数据值可能会意外地改变(宇宙射线任何人),并且它是一个勇敢的现实世界的工程师,不防止意外。

包含注释/* Can never reach here */defaultelse子句实际被访问的频率是多少?

最新更新