使用条件运算符比较两个无符号值时的有符号/无符号不匹配

时间:2012-10-11 10:34:54

标签: c visual-c++ conditional-operator

我有以下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进行测试,并且没有任何警告。

3 个答案:

答案 0 :(得分:7)

这可能是由算术转换规则引起的:首先,任何整数类型的转换排名小于int(例如unsigned char)都会提升为intunsigned int

结果是int还是unsigned int是否(直接)取决于原始类型的签名,但其范围:int即使对于无符号类型也是如此因为可以表示所有值,这是主流架构上unsigned char的情况。

其次,由于两个操作数最终都具有相同的转换等级,但其中一个是无符号的,另一个操作数也将转换为无符号类型。

从语义上讲,你的表达式是

a < (unsigned int)(int)b

a < (unsigned int)(b ? (int)b : (int)c)

编译器显然足够聪明,可以注意到第一种情况不会导致问题,但第二种情况会失败。

Steve Jessop的评论很好地解释了这可能发生的原因:

  

我想在第一种情况下,编译器认为,“我有一个比较运算符,其操作数类型为unsigned intunsigned char。不需要警告,现在让我们应用促销,然后通常转换”。

     

在第二种情况下,它认为,“我有一个比较运算符,其操作数类型为unsigned intint(我将其作为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类型的左值。