C语言 如何保护枚举赋值



我想防止无效的值枚举赋值。我知道,如果我甚至分配值不是在enum它会工作。例子:

enum example_enum
{
    ENUM_VAL0,
    ENUM_VAL1,
    ENUM_VAL2,
    ENUM_VAL3
};
void example_function(void)
{
  enum example_enum the_enum = ENUM_VAL3; // correct
  the_enum = 41; // will work
  the_enum = 0xBADA55; // also will work
  bar(the_enum); // this function assumes that input parameter is correct
}

是否有简单有效的方法来检查对enum的赋值是否正确?我可以通过函数

测试值
void foo(enum example_enum the_enum)
{
  if (!is_enum(the_enum))
    return;
  // do something with valid enum
}

我可以用下面的方法解决这个问题:

static int e_values[] = { ENUM_VAL0, ENUM_VAL1, ENUM_VAL2, ENUM_VAL3 };
int is_enum(int input)
{
  for (int i=0;i<4;i++)
    if (e_values[i] == input)
      return 1;
  return 0;
}

对我来说,我的解决方案是低效的,如果我有更多的枚举和枚举中的更多值,我怎么能写这个?

只要enum是连续的,就可以这样做:

static int e_values[] = { ENUM_VAL0, ENUM_VAL1, ENUM_VAL2, ENUM_VAL3, ENUM_VAL_COUNT };
int is_enum(int input) { return 0 <= input && input < ENUM_VAL_COUNT; }

另一种选择是事先不验证枚举值,但一旦代码检测到无效值就会出错:

switch(input) {
    case ENUM_VAL0: ... break;
    case ENUM_VAL1: ... break;
    ...
    default:
        assert(0 && "broken enum"); 
        break;
} 

但是没有办法强制enum值在c中不超出范围。如果您想确保enum不受干扰,您可以做的最好的事情是将值隐藏在struct中,然后使用函数来操作struct。通过.h文件中的前向声明和.c文件中的实现,可以对用户隐藏函数和struct的实现:

struct example_t {
     enum example_enum value;
}
void example_set_val0(example_t* v) { v->value = ENUM_VAL0; }

对于赋值适合枚举的整数没有任何警告。

C语言中的枚举数是整数类型的同义词。假设为enum example_enum选择的类型是int,那么您的代码与以下代码相同:
void example_function(void)
{
  int the_enum = ENUM_VAL3; // correct
  the_enum = 12345; // will work
  bar(the_enum); // this function assumes that input parameter is correct
}
void foo(int the_enum)
{
  if (!is_enum(the_enum))
    return;
  // do something with valid enum
}

你可以使用结构体,但即使这样也可以避免:

struct example_enum_struct e = { 12345 };
e.value = 23456;

基本上,如果您想将类型限制为特定的值,则需要执行检查。

如果有人对这个话题感兴趣,这里我有一些有效的解决方案。

typed_enums.h

#ifndef TYPED_ENUMS_H
#define TYPED_ENUMS_H
#define TYPED_ENUM(name_) 
    typedef struct { int v; } name_
#define TYPED_ENUM_VALUE(name_, value_)       (name_) { value_ }
#define GET_TYPED_ENUM_VALUE(en_)             (en_.v)
#define TYPED_ENUM_EQ(a_, b_)                 (GET_TYPED_ENUM_VALUE(a_) == GET_TYPED_ENUM_VALUE(b_))
#endif

usb_class.h

#ifndef USB_CLASS_H
#define USB_CLASS_H
#include "typed_enums.h"
TYPED_ENUM(UsbClass);
#define USB_CLASS_BILLBOARD                     TYPED_ENUM_VALUE(UsbClass, 0x11)
#define USB_CLASS_TYPE_C_BRIDGE                 TYPED_ENUM_VALUE(UsbClass, 0x12)
#define USB_CLASS_DIAGNOSTIC_DEVICE             TYPED_ENUM_VALUE(UsbClass, 0xDC)
#define USB_CLASS_WIRELESS_CONTROLLER           TYPED_ENUM_VALUE(UsbClass, 0xE0)
#endif

usb_class_example.c

#include "typed_enums.h"
#include "usb_class.h"
#include <stdio.h>
int main(int argc, char ** argv)
{
    UsbClass usbClass = USB_CLASS_WIRELESS_CONTROLLER;
    usbClass = 12345; // tadam!!!! throws error
    usbClass = USB_CLASS_VIDEO;
    if (TYPED_ENUM_EQ(usbClass, USB_CLASS_VIDEO)) {
        printf("usbClass = USB_CLASS_VIDEOn");
    }
    printf("usb class value: %02Xn", GET_TYPED_ENUM_VALUE(usbClass));
    return 0;
}

优点:

  • 枚举值赋值与结构体赋值类似
  • 指针的enum也可以使用
  • 枚举值不能更改

缺点:

  • 不能在
  • 开关中使用
  • 不能直接比较
  • 不能直接返回enum数值

注意:抱歉这里滥用了预处理器

最新更新