我注意到libc
中的大多数预定义值都是使用#define
指令编写的。例如,whence
参数在int
中占用fseek
,其中(据我所知)enum
会更好。有很多这样的例子显然应该出于某种原因(除了后兼容性问题)。
所以我想知道在哪种情况下使用#define
更好,而enum
是type-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)匿名枚举(如我的例子所示)。
答案 0 :(得分:5)
有很多这样的例子显然应该存在 原因
一个原因是没有可移植的方法从汇编代码中引用C enum
。低级代码通常(通常仍然是)用汇编语言编写,因此低级代码使用的常量将由#define
而不是enum
指定。
另一个原因是成语if (verbosity & WAKE_THE_NEIGHBOURS)
,其中#defined
值用于指定位位置。
所以我想知道在哪种情况下使用#define更好 枚举是一种更容易被发现的类型安全的替代方案
在所有其他情况下,我(今天 - 使用#define
也有点传统)使用enum
,以便if (verbosity == RED)
会引发警告(如果您使用例如{ {1}}与gcc
)。
答案 1 :(得分:3)
虽然人们可能应该使用其他可用的解决方案,但仍然存在需要宏的情况。有些是历史原因,但今天仍有其他原因。
例如,您提到whence
的{{1}}参数(即fseek
,SEEK_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程序相同的东西)。