我刚刚在我的C程序中遇到了一个错误,尽管我能够找出错误的原因,但我很难合理化编译器的行为,我可以在这里使用一些帮助。我正在努力将一系列值转移到负值,但面临符号转换,导致它导致整数溢出。总而言之,这并不是太令人兴奋了,但这是因为编译器决定应对枚举变量进行未签名,该变量也用于计算中,然后导致该结果的输出也被胁迫到未签名的INT。
最小可再现的代码:
#include <stdio.h>
enum UnexpectedlyUnsignedEnum {
VALUE1 = 5
};
int main(int argc, char *argv[]) {
enum UnexpectedlyUnsignedEnum enum_value = VALUE1;
int value1 = (1 - enum_value) / 2;
int value2 = (1 - VALUE1) / 2;
printf("%i\n", value1);
printf("%i\n", value2);
}
导致以下输出:
❯ ./example
2147483646
-2
❯
我可以在某种程度上理解编译器试图“优化” unsigned int的类型,因为它的价值都是积极的(我不确定我同意这是在这里做出的好选择,因为它似乎很可能会引入不必要的错误我可以看到这没有任何好处,何时不需要额外的正整数范围),但是我仍然希望Value1和Value2彼此相等。他们对我而言并不意味着将变量enum_value转换为未签名的值的存储,而Value1仍在签名中。此外,在使用GCC编译时,我会在启用时会得到符号转换警告,但是尽管有标志并产生与上述相同的输出,但Clang并未提供警告:
GCC
❯ gcc --version
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
...
❯ gcc -Wconversion -o example example.c
example.c: In function ‘main’:
example.c:9:18: warning: conversion to ‘int’ from ‘unsigned int’ may change the sign of the result [-Wsign-conversion]
9 | int value1 = (1 - enum_value) / 2;
|
❯
clang
❯ clang --version
Ubuntu clang version 18.1.8 (++20240731024944+3b5b5c1ec4a3-1~exp1~20240731145000.144)
...
❯ clang -Wsign-conversion -o example example.c
❯
简而言之,有人可以帮助我理解这里是否破裂了,还是我对这些看起来差异的理解?
Edit:解决问题不是我的问题,它已经解决了。我想理解为什么编译器这样做,因为它对我似乎是违反直觉的。
由于您可能还没有使用C 2024,C 2018表示,6.4.4.3 2:
再次,在6.7.2.2 3:
的常数
枚举列表中的标识符被声明为具有类型int
…
为枚举本身而言,6.7.2.2 4说:
EACH枚举类型应与char,签名的整数类型或未签名的整数类型兼容。类型的选择是实施定义的,但应能够代表枚举所有成员的价值。
the,int
是
VALUE1
,而有效地符合C 2018,只要实施已记录了后者。