我正在查看微控制器上ADC的头文件,其中包含以下代码。
/**
* ADC channels type.
*/
typedef enum {
ADC_CH_0,
ADC_CH_1,
ADC_CH_2,
ADC_CH_3,
ADC_CH_4,
ADC_CH_5,
ADC_CH_6,
} adc_channel_t;
在 ADC 的 main.c 中,有以下代码行
adc_channel_t channels[] = {ADC_CH_4, ADC_CH_5};
我想知道为什么需要为 ADC 声明新的数据类型? typedef enum 是什么意思?
谢谢
作为artm答案的补充,typedef
被添加到enum
前面,以方便使用enum
。声明是否如下所示:
enum adc_channel_t {
ADC_CH_0,
ADC_CH_1,
ADC_CH_2,
ADC_CH_3,
ADC_CH_4,
ADC_CH_5,
ADC_CH_6,
};
然后,adc_channel_t channels[] = {ADC_CH_4, ADC_CH_5};
行必须写成:
enum adc_channel_t channels[] = {ADC_CH_4, ADC_CH_5};
typedef
允许我们在每次使用该类型时忽略enum
。
使用有用的常量通常比"幻数"更受欢迎,尽管在这种情况下常量几乎没有提供额外的信息,这似乎有点奇怪。但是,它可能很有用,因为枚举器用作额外的描述。例如,您的 IDE 需要 adc_channel_t
类型的值,它将能够建议通道:通过ADC_CH_6
ADC_CH_0
可能是值的有效范围,而不是简单地告诉您使用数字。
为什么这样做有多个组成部分。
- typedef 是做什么的
- 为什么要对枚举进行类型定义
typedef
允许您定义新类型。这使您可以更清楚地表达您的意图。
char id; // but my id is a number!
typedef unsigned char uint8; // ok now we can use uint8 instead of char
uint8 id; // better! Now we know we should only interpret id as a number.
在 C 中,您需要始终指定enum
和struct
引用它们:
enum CarModels_e {
Bravia,
Uno
};
enum CarModels_e the_model;
enum CarModels_e other_model
// this gets boring fast...
所以程序员(懒惰的人(会尽量避免打字太多。为此,他们创建了一个新类型:
typedef enum CarModels_e CarModels; // now we don't need to type enum every time!
CarModels the_model;
CarModels other_model
// Less typing! Happier programmer!
但是您仍然必须键入两次enum CarModels_e
以便它们也将其组合在一个声明中:
typedef enum /* Don't even need a name here anymore! Much less typing :) */ {
Bravia,
Uno
} CarModels;
<小时 />为什么直接使用enum
而不是数字?因为这样你就可以赋予数字以意义。因此,下次他们(或其他任何人(阅读他们的代码时,他们仍然知道这些数字是关于什么的。
// Example
if (measured_frequency == 12345) { // hmm where does this come from?
if (measured_frequency == MaximumMotorFrequency) { // Ah! Now i'll know what this is about in ten years!
我想知道为什么需要为 ADC 声明新的数据类型? typedef enum 是什么意思?
typedef enum
是一种对相关常量进行分组的方法。相反,您可以声明从 ADC_CH_0
到 ADC_CH_6
的七个const
,但由于它们都是相关的,因此最好在此处使用 enum
(默认情况下,每个enum
常量增加 1(。
稍后可以使用类型 adc_channel_t
来声明变量,并保证范围在声明的枚举常量内。
我的首选方法如下:
typedef enum powerState_tag {
PS_OFF,
PS_ON
} powerState_te;
这会在一个表达式中执行多项操作。
- 声明枚举pwerState_tag {PS_OFF, PS_ON}。
- 创建 powerState_te 的类型定义。
我通常会添加pwerState_tag以便某些代码解析器/编辑器将枚举显示为未命名。我添加后缀以指示_tag是枚举的名称,_te是类型化的枚举。为了对称,我在_tag和_te中复制了这个名字。这些不是标准化的命名约定,可以命名为您想要的任何名称,除非您的编码标准另有说明。巴尔集团有一个在线标准,我在几个工作中看到了参考,而且非常好。不过,他们不会使用_te来识别它是什么类型的 typedef。
以这种方式定义类型枚举的最大好处是,如果有人传入未定义的值,编译器会在编译时通知您。
例如,在上面的示例中,以下内容会导致编译时错误:
adc_channel_t channels[] = {99, -1};
但以下内容不会失败,因为枚举中包含 4 和 5:
adc_channel_t channels[] = {4, 5};
一些编辑器也使用 typedef 枚举来自动填充。
adc_channel_t channel;
// when you begin to assign the value, the editor will offer suggestions
channel = AD \<auto completes up to _> and you have to merely type the number of click the value you like.
我使用过的大多数不使用 typedef 枚举模式的代码都会传入泛型 int,而不会指定它是枚举和利用ADC_CH_4,就像 #define 一样。
uint8_t getAdcVal(uint8_t channel); // A
uint8_t getAdcVal(adc_channel_t channel); //B
虽然 A 在用户使用有效范围时正常运行,但编译器不会像 B 那样在编译时验证它是否在枚举范围内。
巴尔集团的编码标准
让我们分别了解typedef
和enum
。
枚举
它是一种用户定义的数据类型,用于将名称分配给整数常量。就像#define
,但两者之间有区别他们,我们将在答案的后面部分讨论。
请考虑以下示例代码:
enum bool{
FALSE,
TRUE
};
这里发生的事情是我们正在定义一种 bool 类型的数据类型,它有两个名称 - FALSE 和 TRUE。编译器初始化这些常量的值,下一个名称的值相对于前一个值递增 1。在我们的例子中,FALSE 的值将为 0,TRUE 的值将为 1。如果我们赋值 FALSE 为 6,则 TRUE(在 FALSE 之后(的值将是 FALSE++,即 7。我们可以自己初始化值,数据结构中的两个或所有名称可以具有相同的值。问题是,当我们有#define
时,为什么要使用枚举?答案是因为:
- 枚举
- 是作用域的本地枚举
- 编译器初始化值注意:所有名称在作用域中必须是唯一的,并且只能是完整的允许常量作为名称的值。
类型定义
考虑我们的示例,其中我们已经定义了数据类型,现在我们想在代码中使用它。您可以使用它的方法是:
enum bool var; //declared a variable var of type enum bool
var = FALSE;
这种声明的问题是,每次要声明时,都必须在该类型的变量之前键入enum bool
。所以我们要做的是给数据类型(enum bool(一个昵称或别名,每当我们想在代码中使用它时,我们都可以声明一个变量,该变量的类型是赋予数据类型的昵称。typedef
关键字用于为数据类型提供昵称。例:
typedef enum bool{
FALSE,
TRUE
} BoolTypedef_t;
因此,每当我们想使用这种数据类型中的任何成员时,我们都可以像这样使用它:
BoolTypedef_t var;
var = TRUE;
printf("%d", var);