我有以下C代码:
unsigned int a;
unsigned char b, c;
void test(void) {
if (a < b)
return;
if (a < (b ? b : c))
return;
}
当我编译它(使用Microsoft cl
,来自MS SDK 7,-W3
警告级别)时,第二次比较会发出警告:C4018,签名/无符号不匹配。第一次比较没有发出警告。
我检查了MS docs on the conditional operator,他们说如果两个操作数的类型相同,那么结果将是相同的类型,因此它应该作为第一个比较。我错过了什么吗?
UPD:使用gcc -Wall -Wextra -pedantic
进行测试,并且没有任何警告。
答案 0 :(得分:7)
这可能是由算术转换规则引起的:首先,任何整数类型的转换排名小于int
(例如unsigned char
)都会提升为int
或unsigned int
结果是int
还是unsigned int
是否(直接)取决于原始类型的签名,但其范围:int
即使对于无符号类型也是如此因为可以表示所有值,这是主流架构上unsigned char
的情况。
其次,由于两个操作数最终都具有相同的转换等级,但其中一个是无符号的,另一个操作数也将转换为无符号类型。
从语义上讲,你的表达式是
a < (unsigned int)(int)b
和
a < (unsigned int)(b ? (int)b : (int)c)
编译器显然足够聪明,可以注意到第一种情况不会导致问题,但第二种情况会失败。
Steve Jessop的评论很好地解释了这可能发生的原因:
我想在第一种情况下,编译器认为,“我有一个比较运算符,其操作数类型为
unsigned int
和unsigned char
。不需要警告,现在让我们应用促销,然后通常转换”。在第二种情况下,它认为,“我有一个比较运算符,其操作数类型为
unsigned int
和int
(我将其作为RHS上条件表达式的类型派生)。最佳警告该!”
答案 1 :(得分:3)
if (a < b)
等于伪if (unsigned int < unsigned char)
。
每当在表达式中使用char类型时,C中的整数提升规则会隐式将其转换为int
。完成后,你有
if (unsigned int < int)
。
每当表达式中使用两个具有相同排名但签名不同的整数时,已签名的整数将被隐式转换为无符号。这取决于通常的算术转换,又名 balance 。
所以你的第一个表达式转换为
if (unsigned int < unsigned int)
。
在第二个表达式中,我们有if (a < (b ? b : c))
等于伪
if (unsigned int < (unsigned char ? unsigned char : unsigned char))
。
对所有字符执行整数提升,因此会隐式转换为
if (unsigned int < (int ? int : int))
。
然后,条件运算符的一个奇怪的,模糊的规则要求?:运算符的第二个和第三个运算符必须与通常的算术转换平衡。在这种情况下,它们已经是相同的类型,所以没有任何反应。我们最终得到了
if (unsigned int < int)
再次发生平衡,结果将被评估为
if (unsigned int < unsigned int)
当我用Microsoft
编译它时
当您使用Microsoft进行编译时,所有投注均已关闭,其编译器在遵循标准时非常差。期待奇怪的,不合逻辑的警告。
答案 2 :(得分:1)
C和C ++之间的规则不同。在使用MSVC编译C时,很难判断适当的标准,但幸运的是,在这种情况下,C89和C99是相同的。
在C89 3.3.15:
如果第二个和第三个操作数都有算术类型,那么通常 执行算术转换以将它们带到公共类型 结果有那种类型
在C99 6.5.15 / 5中:
如果第二个和第三个操作数都有算术类型,则结果 由通常的算术转换确定的类型, 他们应用于这两个操作数,是结果的类型。
在C ++ 03 5.16 / 4中:
如果第二个和第三个操作数是左值并且具有相同的类型, 结果是该类型,并且是左值
所以当你说,“如果两个操作数是相同的类型,结果将是相同的类型,所以它应该作为第一个比较”,只适用于C ++,而不是C。在C中该比较的RHS类型为int
。在C ++中,RHS将是您所期望的unsigned char
类型的左值。