c - 在 C99 中是否有符合 MISRA 标准的方式来使用枚举标志?
问题描述
我有一个正在 C99 中开发的项目,我正在尝试使其符合 MISRA 2012 标准。
在一个文件中,我定义了一个枚举,其中每个值都应被视为一个标志:
/**
* Enumerates the configurable options for performing calibration.
*/
typedef enum
{
CALIBRATION_DEFAULT_OPTIONS=0, /**< Calibrate with default options */
CALIBRATION_RESET_POSITION=1, /**< Ensure window is fully open and motor re-homed */
CALIBRATION_FORCE_RECALIBRATE=2 /**< Force recalibration even if calibration data exists */
} CALIBRATION_OPTIONS_T;
我希望能够声明如下内容:
CALIBRATION_OPTIONS_T options = CALIBRATION_RESET_POSITION | CALIBRATION_FORCE_RECALIBRATE;
我还定义了一个函数,它接受CALIBRATION_OPTIONS_T
参数并根据设置的标志执行不同的逻辑:
// If forced to recalibrate, do so regardless of whether metrics exist in
// EEPROM or not.
if ((options & CALIBRATION_FORCE_RECALIBRATE) != 0U)
{
MOTION_ResetCalibrationData();
calibration = performCalibrationRoutine();
}
// Otherwise try fetching existing metrics from EEPROM. If they exist, return
// these metrics.
else if (tryFetchStoredMetrics(&calibration))
{
if ((options & CALIBRATION_RESET_POSITION) != 0U)
{
calibration.lastPosition = 0;
resetMotorPosition();
storeMetrics(calibration);
}
}
但是,当我使用 PC-lint Plus 对项目进行 lint 时,我得到以下输出,说明此代码违反了 MISRA 2012 规则 10.1:
if ((options & CALIBRATION_FORCE_RECALIBRATE) != 0U)
~~~~~~~ ^
*** LINT: src\c\motionCalibrator.c(645) note 9027: an enum value is not an appropriate left operand to & [MISRA 2012 Rule 10.1, required]
if ((options & CALIBRATION_FORCE_RECALIBRATE) != 0U)
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*** LINT: src\c\motionCalibrator.c(645) note 9027: an enum value is not an appropriate right operand to & [MISRA 2012 Rule 10.1, required]
if ((options & CALIBRATION_FORCE_RECALIBRATE) != 0U)
^
*** LINT: src\c\motionCalibrator.c(645) warning 641: implicit conversion of enum 'CALIBRATION_OPTIONS_T' to integral type 'unsigned int'
if ((options & CALIBRATION_RESET_POSITION) != 0U)
~~~~~~~ ^
*** LINT: src\c\motionCalibrator.c(655) note 9027: an enum value is not an appropriate left operand to & [MISRA 2012 Rule 10.1, required]
if ((options & CALIBRATION_RESET_POSITION) != 0U)
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~
*** LINT: src\c\motionCalibrator.c(655) note 9027: an enum value is not an appropriate right operand to & [MISRA 2012 Rule 10.1, required]
if ((options & CALIBRATION_RESET_POSITION) != 0U)
^
*** LINT: src\c\motionCalibrator.c(655) warning 641: implicit conversion of enum 'CALIBRATION_OPTIONS_T' to integral type 'unsigned int'
特别是,MISRA 2012 标准建议不要使用&
枚举,原因有以下两个:
本质上为枚举类型的操作数不应在算术运算中使用,因为枚举对象使用实现定义的整数类型。因此,涉及枚举对象的操作可能会产生意外类型的结果。请注意,来自匿名枚举的枚举常量本质上是有符号类型。
移位和按位运算只能对本质上无符号类型的操作数执行。将它们用于本质上有符号的类型所产生的数值是实现定义的。
我想知道是否有一种符合 MISRA 的方式,我可以使用类似标志的枚举并测试是否设置了特定的标志。
解决方案
这归结为基本类型模型和规则 10.1。您只能对本质上无符号的类型进行按位运算。MISRA-C 将枚举视为它们自己的唯一类型。
做类似CALIBRATION_OPTIONS_T options = CALIBRATION_RESET_POSITION | CALIBRATION_FORCE_RECALIBRATE;
的事情在其他方面很好而且很规范 C,但你必须求助于使用无符号常量。要将类型安全性发挥到极致,您可以这样做:
typedef uint32_t CALIBRATION_OPTIONS_T;
#define CALIBRATION_DEFAULT_OPTIONS ((CALIBRATION_OPTIONS_T)0x00u) /**< Calibrate with default options */
#define CALIBRATION_RESET_POSITION ((CALIBRATION_OPTIONS_T)0x01u) /**< Ensure window is fully open and motor re-homed */
#define CALIBRATION_FORCE_RECALIBRATE ((CALIBRATION_OPTIONS_T)0x02u) /**< Force recalibration even if calibration data exists */
其中十六进制表示法是自记录代码,表明这些是位掩码,u
MISRA 在某些情况下需要后缀,并且uint32_t
可以阻止潜在的隐式类型提升。
请注意,使用枚举不一定会提高类型安全性,而是相反。在许多情况下,它们被视为 plain int
,在其他情况下被视为实现定义的大小整数。C 语言设计几乎破坏了它们的类型安全性。尽管您可以通过一些技巧使它们安全,但请参阅我在如何创建类型安全枚举中的帖子?.
推荐阅读
- javascript - 如何结合渲染和作曲家?
- javascript - CryptoJS 使用实际密钥,而不是密码
- python-3.x - 如何检查在 macOS 后台终端上运行的 Python3 进程?
- docker - 忽略一个 docker 文件的目录,而不是另一个
- python-3.x - Stanza Stanford NLP:无法从“spacy_stanza”导入名称“StanzaLanguage”
- wpf - 为什么在 Visual Studio 2019 中不会生成 WPF 表单的 .g.vb 文件?
- python - python 5 (dim 1) != 1 (dim 0)
- django - 如何在多对多领域 django 中获取最新的 id
- php - Woocommerce 附加信息选项卡:添加产品自定义字段值
- python - 验证检查不包括增值税 - 增值税和总金额