问题:
为什么我必须强制转换枚举元素时,分配给相同的枚举变量类型在C?
我在这段代码中遇到了问题,因为它没有满足MISRA C 2012规则10.3,该规则规定:
表达式的值不能赋给具有较窄基本类型或不同基本类型类别的对象
代码如下:
typedef enum
{
FLS_PROG_SUCCESS,
FLS_PROG_FAIL,
FLS_ERASE_SUCCESS2U,
FLS_ERASE_FAIL,
FLS_READ_SUCCESS,
FLS_READ_FAIL,
FLS_FORMAT_SUCCESS,
FLS_FORMAT_FAIL
}FLS_JobResult_t;
void Foo(void)
{
FLS_JobResult_t ProgramStatus;
/* Then I try to initialize the variable value */
ProgramStatus = FLS_PROG_SUCCESS;
...
}
我接受了一个暗示这个工具可能有缺陷的答案。我仍然相信这一点,但是为了修复这个问题,我在类型定义枚举声明中加入了名称现在是:
typedef enum FLS_JobResult_tag
{
FLS_PROG_SUCCESS,
FLS_PROG_FAIL,
FLS_ERASE_SUCCESS2U,
FLS_ERASE_FAIL,
FLS_READ_SUCCESS,
FLS_READ_FAIL,
FLS_FORMAT_SUCCESS,
FLS_FORMAT_FAIL
}FLS_JobResult_t;
据我所知两者是完全一样的。但是,令人惊讶!错误消失!规则检查器不再将其标记为错误!!
然后做了一些研究,我发现了这两个问题:
这两种类型在C语言中的区别是什么?
和
这两种枚举声明的区别是什么?
我意识到在匿名枚举和命名枚举之间有细微的区别。但是没有什么能说明规则检查员抱怨一种形式的另一种形式的原因。
所以问题是:匿名枚举与可能违反MISRA c 2012规则10.3的命名枚举的区别是什么?
两个例子都是兼容的,并且都是出于相同的原因:它们不是赋值不同基本类型的对象。
让我们澄清一下。
C在其类型系统中为开发人员/编译器提供了很大的自由,但它也可能导致意想不到的结果,可能会丢失值、符号或精度。MISRA-C:2012通过其基本类型模型帮助实施更安全的类型,该模型为控制类型转换的使用和提高对实现特定行为的认识提供了规则定义的合理基础。x规则)。
基本类型模型取代了MISRA-C:2004标准的"底层类型"模型(由于一个原因,它导致了许多程序员强制执行不必要的强制类型转换)。
我怀疑你的工具是混乱的,和/或部分坚持旧的模型。
与枚举相关的基本类型规则识别两种不同的编程用途:
- 枚举类型的对象,旨在与带有不同的枚举类型。
- 枚举是保存一组整型常量的常用方法。
C标准没有给出区分这些用法的方法。因此MISRA-C:2012 增加了以下不同的基本枚举类型(同时不影响C的行为):
- 命名枚举类型 -在此定义的枚举的定义中使用的标记或类型定义来标识任何对象、函数或类型;如果为整数,则必须使用强制类型转换需要枚举常量的值。
- 匿名枚举类型—未在任何对象、函数或类型的定义。这通常是用于定义一组常量,这些常量可能是相关的,也可能是不相关的,但避免了铸造的需要。
匿名枚举类型的示例:
enum {D = 10, E = 20, F = 30};
你的两个例子都是命名的枚举类型(它们是兼容的,因为它们是相同的基本类型)。其他例子有:
enum JOHN {A, B, C};
enum PAUL {E, F, G} PAUL;
因此,真正违反10.3的一个例子是:
enum PAUL bar = B;
参考文献:MISRA-C:2012附录D.5"基本类型的枚举"用其他例子很好地放大了这一点。 对于lside (type is anonymous enumeration) = rside (type is anonymous enumeration)
,左右不知道它是相同的匿名枚举-因此存在潜在的问题。
对于lside (type is named enumeration) = rside (type is _same_ named enumeration)
- all is OK,已知使用了相同的枚举
真正的"bug"实际上在C标准(6.7.2.2)中。C保证枚举常量是int
类型,但是枚举变量可以是许多不同的类型,例如char
。
至于枚举常量和枚举变量使用哪一种基本类型,在MISRA-C:2012附录D.6中有描述。代码中的枚举常量被认为与命名的枚举类型具有相同的基本类型。
所以这个工具是不正确的,不应该发出诊断
给enum定义类型(几乎)总是一个坏主意。
不如这样写:
enum FLS_JobResult_t
{
FLS_PROG_SUCCESS,
FLS_PROG_FAIL,
FLS_ERASE_SUCCESS2U,
FLS_ERASE_FAIL,
FLS_READ_SUCCESS,
FLS_READ_FAIL,
FLS_FORMAT_SUCCESS,
FLS_FORMAT_FAIL
};
void Foo(void)
{
enum FLS_JobResult_t ProgramStatus;
/* Then I try to initialize the variable value */
ProgramStatus = FLS_PROG_SUCCESS;
}