在枚举之间选择还是在C中定义?

时间:2016-05-17 07:59:36

标签: c enums casting

我注意到libc中的大多数预定义值都是使用#define指令编写的。例如,whence参数在int中占用fseek,其中(据我所知)enum会更好。有很多这样的例子显然应该出于某种原因(除了后兼容性问题)。

所以我想知道在哪种情况下使用#define更好,而enumtype-safe alternative more easily discoverable

作为一个实际示例,考虑表示输入/输出通道的typedef,因为内核可能就是这种情况。 gpio可以在输入或输出中配置。在这里使用enum指令是否值得?

typedef struct gpio {
    size_t port; 
    size_t bit;
    enum { DIR_INPUT, DIR_OUTPUT } direction; // or `bool`?
    bool value;
} gpio;

请注意enum可以用三种不同的方式实现:

i)输入:

typedef enum gpio_direction {   
   DIR_OUTPUT
   DIR_INPUT
} gpio_direction;

ii)全球枚举

enum gpio_direction {   
   DIR_OUTPUT
   DIR_INPUT
} gpio_direction;

iii)匿名枚举(如我的例子所示)。

2 个答案:

答案 0 :(得分:5)

  

有很多这样的例子显然应该存在   原因

一个原因是没有可移植的方法从汇编代码中引用C enum。低级代码通常(通常仍然是)用汇编语言编写,因此低级代码使用的常量将由#define而不是enum指定。

另一个原因是成语if (verbosity & WAKE_THE_NEIGHBOURS),其中#defined值用于指定位位置。

  

所以我想知道在哪种情况下使用#define更好   枚举是一种更容易被发现的类型安全的替代方案

在所有其他情况下,我(今天 - 使用#define也有点传统)使用enum,以便if (verbosity == RED)会引发警告(如果您使用例如{ {1}}与gcc)。

答案 1 :(得分:3)

虽然人们可能应该使用其他可用的解决方案,但仍然存在需要宏的情况。有些是历史原因,但今天仍有其他原因。

例如,您提到whence的{​​{1}}参数(即fseekSEEK_SET和“SEEK_END”。这些(由于历史原因)被指定为标准中的宏 - 因此实现者必须将它们定义为完全兼容的宏,一些邪恶的程序员可以编写(并责怪库的后果):

SEEK_CUR

但据我所知,他们没有理由不写:

#include <stdio.h>

#ifndef SEEK_CUR
int evil = *(int*)0;
#endif

另一个历史原因是编译器在使用宏时可能产生了更高效的代码。当时写的代码可能仍然存在并且包含当时工作得更好的构造(今天仍然很好用)。

现代原因是您需要使用C编译器之外的值。例如,如果您想在汇编程序代码(或其他语言)中使用该值。您需要做的就是确保标题不会扩展为某些内容(包含C代码),除非它是用C编译器编译的。例如:

enum __WHENCE_T {
    __SEEK_CUR,
    __SEEK_END,
    __SEEK_SET
};

#define SEEK_CUR __SEEK_CUR
#define SEEK_END __SEEK_END
#define SEEK_SET __SEEK_SET

如果从汇编程序中包含宏,#define NUMBER 42 #ifdef __STDC__ extern int variable; int function(void); #endif 仍然可以扩展(并扩展到与C程序相同的东西)。