【问题标题】:Good use cases for declaring enum type tags and variable names in C?在 C 中声明枚举类型标签和变量名的好用例?
【发布时间】:2020-05-08 16:31:53
【问题描述】:

如果您的代码中需要一些常量,您可以使用枚举声明它们:

enum {
    DOG,
    CAT,
    FISH,
};

enum {
    CAR,
    BUS,
    TRAIN,
};

然后根据需要使用DOGBUS 等。但是枚举也可以以更冗长的方式声明:

enum animals {
    DOG,
    CAT,
    FISH,
} pets;

enum transport {
    CAR,
    BUS,
    TRAIN,
} vehicles;

鉴于枚举常量具有全局范围并且不能像结构和联合那样被pets.DOG 引用,那么详细样式有什么好的用例吗?对我来说,枚举的类型标签和变量名看起来相当多余,甚至令人反感,因为它们看起来像结构,但不能像结构一样使用。我希望我遗漏了一些东西,它们确实有很好的用途。

有一个相关的SO Q&A,其中最重要的假设是在使用枚举时会使用类型标签和变量名。所以我的问题可以重述为“如果我只使用匿名枚举,我会在哪些任务中失败?”因为对我来说,枚举的全部意义在于DOGCATCAR 常量,我认为将其中之一分配给枚举变量是没有用的。我还在学习,所以我确定我一定遗漏了一些东西。

【问题讨论】:

  • @Ctx 请澄清!据我了解和我的测试显示,枚举常量必须具有唯一的名称。我不明白“两个枚举......不同的值......不同的上下文”真正指的是什么。
  • 好的,使用枚举这确实是不可能的
  • 不确定我是否完全理解您的问题,但您使用枚举标签的原因是为了能够稍后引用它(例如,定义枚举类型的变量) - 参考.例如。 Why do C enumeration constants need a name?.
  • 或者你问为什么要定义枚举类型变量?
  • @SanderDeDycker 我问的是类型和变量的可用性。正如我的问题所述,我的印象是枚举对于它们声明的常量很有用。常量是全局的并且必须是唯一的——那么类型和变量有什​​么用呢?一个完整的答案将不胜感激。

标签: c enums declaration


【解决方案1】:

您可以为 enum 类型命名,以防您要声明该类型的变量:

enum animals a1 = DOG;
enum animals a2 = CAT;

或者将它们作为函数参数:

void foo(enum animals a);

虽然枚举被视为整数类型,您也可以使用int 来存储其中一个值,但使用enum 类型的变量有助于记录您的代码并使您的意图向读者清晰。

【讨论】:

  • 好的,所以基本上,我可以将枚举常量视为整数,无论​​是变量声明还是函数参数。枚举类型和变量的用处在于它们的“评论”属性,即意图的路标。仅此而已,对吧?
  • @Theo 一些编译器还提供有用的警告以避免错误,例如enum animals a1 = TRAIN;
  • @user694733 这很有趣,但假设我有理由将枚举常量分配给特定的枚举变量。我的问题是我为什么要这样做?毕竟,我声明枚举是为了获得一些现成的常量,如DOGTRAIN 等。为什么要使用枚举类型和变量名呢?
  • @Theod'Or :变量的典型用法是当您在编译时不知道该值时(或者因为它基于输入,或者是函数的参数,或者... )。这适用于所有变量,包括枚举类型的变量。
  • @SanderDeDycker 请发布一个完整的答案,显示由于未使用枚举类型和变量名而导致的失败。关于变量的通用断言没有帮助,特别是当枚举的全部意义在于它们用作编译时常量时,具有全局范围。
【解决方案2】:

“对我来说,枚举的类型标签和变量名看起来很多余……”

以枚举列表 (enum) 的形式使用命名整数值的顺序集合的价值乍一看似乎很微妙,但在 C 项目中使用时会变得非常明显,原因有以下几点:

  • 与枚举列表关联的名称提供 自记录代码,即特别是在收集名称时 选择代表一组枚举值形成一个相关的主题 到手头的任务。 (你的动物 enum 就是一个很好的例子,因为 将用于枚举例如。大量命令,或 公司内的职位类型。)
  • 枚举列表中值的默认分配是 顺序,从 0 开始,递增 1 直到结束 列表,生成唯一值列表,非常适合 在通过特定字符串数组索引时使用 含义,或者在 switch 语句中用作常量整数时 每个case 语句的值。

关于评论:"...但是使用ANML 类型而不是int 的优势是最小的,..."

  • enum 列表还提供了文档化的约束。例如使用ANML anml; 而不是int anml; 作为struct 成员将很快向那些将维护/更新源代码的人(在未来的几个月或几年内。)有一个相关的列表 该成员被限制使用的相关值,而不是 任何随机整数值。这在枚举列表时很重要 将被使用,例如。在仅用于处理集合的 switch 语句中 与 enum 中的常量整数值相对应的 case 语句。

这两个一起是我发现特别有用的用例的一部分,即使用枚举与字符串数组一起为用户界面选择内容,或用于子字符串搜索等。

例如:

typedef enum {
       CAT,
       DOG,
       FISH,
       MAX_ANML
    }ANML;//for use in struct

 char *strings[MAX_ANML] = {"cat","dog","fish"};

    typedef struct {
        char content[80];
        ANML anml;
    }SEARCH;

例如,这两个结构可以与switch 语句结合使用:

bool searchBuf(SEARCH *animal)
{
    bool res = FALSE;
    switch (animal->anml) {
        case CAT:
            //use the string animal[type] for a search, or user interface content, etc.
            if(strstr(animal->content, strings[CAT]))
                res = TRUE;
            break;
        case DOG:
            if(strstr(animal->content, strings[DOG]))
                res = TRUE;
            break;
        case FISH:
            if(strstr(animal->content, strings[FISH]))
                res = TRUE;
            break;
    };
    return res;
}

int main(void)
{
    char buffer[] = {"this is a string containing cat."};
    SEARCH search;
    strcpy(search.content, buffer);
    search.anml = CAT;

    bool res = searchBuf(&search);
    //use res...

    return 0;    
}

【讨论】:

  • 谢谢,看起来非常好用,但您的代码无法编译。另外,您是否使用枚举类型和变量名?
  • @ryyker 您的代码对于演示如何构建搜索非常有用,但是使用 ANML 类型而不是 int 的优势很小:结构的名称 anml成员使意图足够清楚。我现在已经发布了我自己的答案,将等待更有说服力的答案。
【解决方案3】:

我在调试时喜欢枚举而不是#defines。 debuer 不仅显示数值,还显示枚举名称 - 非常方便

【讨论】:

  • “枚举名称”是指枚举常量,例如我的示例中的DOG,还是该常量可能分配给的枚举变量的名称?
【解决方案4】:

在其他一些人发布了有用的答案和 cmets 之后,我发布了我自己的答案,并且我们已经确定枚举类型名和变量名可用作自记录代码并明确代码的意图(感谢 ryyker 和 dbush 的回答)。

当我在试验并寻找使用非匿名枚举的更有力的理由时,我确定了根据定义不可能有任何枚举。枚举没有范围和边界检查,在编译时(GCC 6)和运行时都没有。这是一个证明弱点的 sn-p:

enum withType { // enum with type name and variable name
    ONE,
    TWO,
    THREE,
} wtEnum;

enum { // Anonymous enum
    TINY,
    SMALL,
    MID,
    LARGE,
    BIGGEST,
};

int main(void) {
    enum withType wt1 = LARGE; // Overflow!
    wtEnum = BIGGEST; // Overflow!
    printf("Enum test values: %d, %d, %d\n", THREE, wt1, wtEnum);
    return 0;
};

该示例清楚地表明,您可能想要对枚举类型名称和变量名称进行的任何“范围界定”都只是按照惯例,并且依赖于编码规则而不是跨越枚举“域”。我什至会声称,鉴于这种现实,C 中的枚举功能是“设计错误的”。它创造了我们在结构和联合中看到的那种实用性的印象,但没有提供任何类型的东西。在此见解之后,我认为匿名枚举是唯一可以使用的安全枚举!

综上所述,我接受了 ryyker 的回答,因为它很好地展示了枚举的主流用法。但我也在这里留下我自己的答案,因为我提出的观点是有效的。

【讨论】:

  • 我通常总是在枚举列表中的最后一个值中包含一个MAX_ 前言,使最后一个值的值 == 到列表中的值的数量。当我计划使用它来创建与集合中的每个 enum 值相对应的字符串集(数组)时,我会这样做。 (您可能已经在我的答案的代码示例中注意到了这一点
  • 您使用//Overflow! cmets 显示的两个实例,除了不太可能出现的用例之外,还说明了编译器警告应该产生影响的实例:warning: integer constant not in range of enumerated type 'enum with Type'。考虑到enum 类型的全部目的,这是一个非常恰当、有用的警告。正如这篇文章的几个地方所述,使用 names 创建一个 range 整数常量值,通常的主题,提供 1)自我记录的代码和 2)一种方式...
  • ...枚举一组有限的条件、属性、执行路径等。
  • @ryyker 使用//Overflow! 我想证明缺少枚举范围,可以将枚举常量分配给任何枚举。我没有收到您提到的警告,但后来我只使用了-Wall。我将研究启用警告的其他选项。
  • 如果您考虑一下,请发表您的发现,并在评论中标记我。大多数优秀的编译器(包括 gcc)都会对这种用法发出警告。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-26
  • 2022-01-23
  • 1970-01-01
相关资源
最近更新 更多