【问题标题】:Is there a MISRA-compliant way to use enum flags in C99?在 C99 中是否有符合 MISRA 的方法来使用枚举标志?
【发布时间】:2018-08-16 08:02:52
【问题描述】:

我正在使用 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 标准建议不要将&amp; 与枚举一起使用,原因有两个:

  1. 本质上为枚举类型的操作数不应用于 算术运算,因为枚举对象使用 实现定义的整数类型。涉及枚举的操作 因此,对象可能会产生具有意外类型的结果。注意 来自匿名枚举的枚举常量基本上已签名 输入。

  2. 移位和按位运算只能对 本质上是无符号类型。使用它们产生的数值 基本上有符号的类型是实现定义的。

我想知道是否有一种符合 MISRA 的方式,我可以使用类似标志的枚举并测试是否设置了特定标志。

【问题讨论】:

  • 使用定义代替标志。枚举不适用于位标志。
  • @Tagc 如果你觉得你真的想在 MISRA 中使用枚举,如果你在对它们进行算术运算之前将它们转换为无符号类型可能会起作用,但我会坚持定义。
  • @Tagc 只是不要写这样错误的代码,你很好;)
  • @Tagc 实际上,您将类型安全与演员一起扔出了窗外。因为它说:忽略它拥有的类型,让它成为我想要的
  • 我所说的“类型安全”是指我的编译器(ARM C 编译器 v5.06)确实如果使用枚举并做一些像 options = CALIBRATION_RESET_POSITION | 8 这样的傻事会警告我.它仍然可以编译,但发出警告总比没有好。而使用定义,编译器甚至不会发出警告。但是,我动摇了,我现在已经改用定义了。我会尽量确保我不会做像CALIBRATION_RESET_POSITION | 8 这样的傻事。

标签: c standards misra


【解决方案1】:

这归结为基本类型模型和规则 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 */

十六进制表示法是自记录代码,表明这些是位掩码,在某些情况下 MISRA 需要 u 后缀,uint32_t 用于阻止潜在的隐式类型提升。

请注意,使用枚举不一定会提高类型安全性,而是相反。在许多情况下,它们被视为普通的int,在其他情况下,它们被视为实现定义的大小整数。 C 语言设计几乎破坏了它们的类型安全性。尽管您可以通过一些技巧使它们安全,但请参阅我在 How to create type safe enums? 的帖子。

【讨论】:

  • 嗯,是的,你说服了我。我将改用这种方法而不是枚举标志。
  • @Tagc 另外请注意,这不仅仅是一些误报; MISRA 规则是一个非常健全的规则。枚举是相当不可移植的,尤其是在小型微控制器系统中,并且经常给你一个签名的结果,这与按位运算一起使用是危险的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-15
  • 2018-10-04
相关资源
最近更新 更多