24

gcc 似乎没有使用以下代码产生警告。我怎样才能让它产生警告?

typedef enum
{
    REG8_A,
    REG8_B,
    REG8_C
}REG8;

typedef enum
{
    REG16_A,
    REG16_B,
    REG16_C
}REG16;

void function(REG8 reg8)
{

}

int main(void)
{
    function(REG16_A);    // Should warn about wrong enum
}
4

6 回答 6

11

要使用 GCC 在 C 中执行此操作-Wenum-compare(如果启用,则默认启用-Wall),您必须在将枚举常量传递给函数之前对其进行比较,以获得所需的诊断。

-Wenum-比较

警告不同枚举类型的值之间的比较。在 C++ 中,条件表达式中的枚举不匹配也会被诊断出来,并且默认情况下会启用警告。在 C 中,此警告由 -Wall 启用。

http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

为了在我们调用函数时自动发生这种比较,我们可以将函数包装在宏中。为了可读性,我还定义了一个宏 SAFE_ENUM,它对枚举常量执行无害的比较(这是在尝试将错误的枚举常量传递给fooor时最终触发警告的原因bar)。

/**
  SAFE_ENUM: evaluate an enumeration constant safely
  TYPE: the enumeration type
  VAL: the enumeration constant to evaluate
*/
#define SAFE_ENUM(TYPE, VAL) ((VAL) == (TYPE)0 ? (VAL) : (VAL))

typedef enum
{
    REG8_DEFAULT,
    REG8_A,
    REG8_B,
    REG8_C
} Reg8;

typedef enum
{
    REG16_DEFAULT,
    REG16_A,
    REG16_B,
    REG16_C
} Reg16;

void foo(Reg8 reg8)
#define foo(reg8) foo(SAFE_ENUM(Reg8, reg8))
{
    printf("%s called with value %d\n", __func__, reg8);
}

void bar(Reg16 reg16)
#define bar(reg16) bar(SAFE_ENUM(Reg16, reg16))
{
    printf("%s called with value %d\n", __func__, reg16);
}

int main(void)
{
    foo(REG8_A);  // ok
    bar(REG16_A); // ok
    foo(REG16_B); // warning
    bar(REG8_B);  // warning

    Reg16 a_reg16 = 42;
    foo(a_reg16); // warning: foo requires a Reg8 but you gave it a Reg16
}
于 2014-01-19T06:53:24.437 回答
9

这种行为的原因是您使用的是 C 编译器而不是 C++。在 C 中,枚举类型并不是真正的类型,C 中的枚举只是保存 int 常量,它们可以与任何整数和任何算术自由混合。

相反,在 C++ 中,您拥有真正的枚举,就像您想的那样,并且所需的类型检查按照语言标准的规定进行。

您的问题可以通过两种方式解决:

  • 使用 C++ 编译器。

    这样,您将拥有真正的 enums,如您所愿。

  • 将您的代码更改为纯 C 风格,即不使用枚举,因为在 C 中它们只是常量集,编译器仅帮助您对常量值进行排序。在 C 语言中,您将负责保持传递的常量的“类型”一致。再一次:对于 C,枚举成员只是 int 常量,你不能让它们被输入。


#define REG8_A 0
#define REG8_B 1
#define REG8_C 2

#define REG16_A 0
#define REG16_B 1
#define REG16_C 2
于 2011-01-17T13:11:47.193 回答
6

我能看到生成警告的唯一方法是,如果您准备传递指针而不是裸枚举,例如

typedef enum
{
    REG8_A,
    REG8_B,
    REG8_C
} REG8;

typedef enum
{
    REG16_A,
    REG16_B,
    REG16_C
} REG16;

void function(REG8 * reg8)
{

}

int main(void)
{
    REG16 r = REG16_A;
    function(&r);
    return 0;
}

不完全是一个优雅的解决方案,但它确实给出了一个警告,至少在gcc -Wall

$ gcc -Wall warn_enum.c -o warn_enum
warn_enum.c: In function ‘main’:
warn_enum.c:23: warning: passing argument 1 of ‘function’ from incompatible pointer type
$
于 2011-01-12T13:54:31.013 回答
2

正如其他人指出的那样,C 不区分枚举类型和底层整数类型。enum(一些编译器可能包括s 或s 的类型检查typedef作为扩展;YMMV。)

要在 C 中进行类型检查,您可以使用structs,但随后您将失去对内置比较运算符的使用以及switch对变量进行操作的能力。不过,您可以尝试这样的事情:

typedef struct {
    enum {
        reg8_val_A,
        reg8_val_B,
        reg8_val_C,
    } val;
} reg8;
#define reg8_A (reg8){.val = reg8_val_A}
#define reg8_B (reg8){.val = reg8_val_B}
#define reg8_C (reg8){.val = reg8_val_C}
…
bool
is_A_or_B(reg8 reg) {
    if reg.val == reg8_A.val    // one way to compare
        return true;
    switch (reg.val) {
        case reg8_val_B:        // the other way to compare; note that
            return true;        // “case reg8_B.val:” will *not* work
        case reg8_val_C:
            return false;
        default:
            fprintf(stderr, "bad reg value %d\n", reg.val);
            abort();
    }
}

(使用一些 C99 功能。)

于 2011-01-18T16:12:30.493 回答
1

使用 GCC 4.6,您应该使用-Wconversion-Werror选项来防止任何隐式类型转换。它给出了 Paul R 发布的代码的错误。但原始代码无论如何编译得很好。我不知道为什么。

于 2011-03-12T11:55:54.337 回答
0
$ g++ test3.cpp -o test3
test3.cpp: In function ‘int main()’:
test3.cpp:22: error: cannot convert ‘REG16’ to ‘REG8’ for argument ‘1’ to ‘void function(REG8)’
于 2011-01-17T12:46:51.643 回答