为什么编译器不喜欢隐式转换为uint类型?

3

我在C++和C#中都遇到了一些关于uint使用的类似问题,现在我想知道其中的原因(每个例子的原因可能完全不同)。对于下面这两个示例,请注意我编译时将警告级别设置为最大。

(1) 在以下代码中,gcc会抱怨比较int和uint,而vc++则不会:

uint foo = <somevalue>;
if( foo == ~0 )  //right here
   ...

在gcc和vc++中,与0进行比较时不需要进行任何强制转换。

(2) 在C# 3.5中,我遇到了类似的问题。以下代码可以正常工作:

uint foo = 1;
uint bar = 2;

但是这会产生uint/int警告:

bool condition = <somevalue>;
uint foo = condition ? 1 : 2; //right here

为什么编译器对立即值的有符号性如此敏感?我完全理解从变量分配时出现的问题,但是这对于立即值来说就毫无意义;在解析中是否存在某种隐藏的困难,阻止了允许这种行为?还是怎么回事?
编辑:是的,我知道我可以在数字后面加上'u',但这回避了我的问题,我的问题是关于将右侧隐含转换为左侧,而不是显式转换右侧。

是的,我知道,但那不是我的问题 :) - Not Sure
7个回答

7
混合使用有符号和无符号值而没有明确的程序员意图可能会导致微妙的错误。虽然int和uint都存储在相同大小(4个字节)的内存位置中,并且它们是位置分配兼容的,但它们的位表示和在常见操作方面的行为是不同的,并且它们也具有不同的范围。
这就像解决一个数学问题并说为什么我不能自由地将区间[-2147483648到2147483647]与[0到4294967295]交换?好吧,你可以,但是如果超出边界,结果可能不正确 :). 这就是为什么编译器要求您进行确认(通过明确),以避免错误地混合不同类型。
此外,在C#中,字面量数字始终是int32,如果您想要其他字面量类型,如float、decimal、ulong等,您需要使用适当的后缀,例如:
uint foo = condition ? 1u : 2u; // uint literals;

编辑:正如Andrew Hare所指出的,C#整数字面量不仅限于int32,而是根据大小而定(int,uint,long,ulong),如此处所述:

C# 整数字面量 - MSDN


4

我无法代表 gcc,但就 C# 3 编译器而言,您需要明确告知它这些 ints 应该是无符号的:

uint foo = condition ? 1U : 2U;

C#编译器喜欢使用int,并假设范围内的所有整数值都是int。由于您的表达式使用了条件运算符,编译器会过于急切地假定您的文字值为int,从而导致赋值失败。


编辑:请注意,我说的是System.Int32范围内的整数值。考虑以下示例:

using System;    

class Program    
{    
    static void Main()    
    {    
        Console.WriteLine(1.GetType());    
        Console.WriteLine(2147483648.GetType());    
    }    
}

Output:

System.Int32
System.UInt32


那么你的意思是C#编译器在看到立即数时就立即分配类型,而不是在实际读取立即数时通过解析树来消除歧义? - Not Sure
@不确定:没错,C#会尝试进行任何隐式转换,但数字字面量始终为int32。 - Pop Catalin
@Pop Catalin:请看我的编辑——你的评论并不完全正确 :) - Andrew Hare
@Andrew:你说得对,我完全忘记了,尽管我以前使用过没有后缀的长字面量 :s ... - Pop Catalin
@Pop Catalin:没关系——我只是在挑剔而已 :) - Andrew Hare

1

让我问你一个问题...假设你有一台大端32位机器,它用2的补码表示数字。哪个无符号整数值等于有符号整数值0xFFFFFFFF?好吧,如果你看到有人这样做,你不会警告吗?


我喜欢你的简洁方法。 (+1)那么正数呢?编译器不能检查它们然后继续吗? - GregC
1
并不是真的。问题只是转移到了另一端。没有任何正有符号整数等于任何高于无符号0x0FFFFFFF(32位,2的补码)的值。所以问题是作为编译器,我应该警告用户他们可能犯了一个错误吗?我曾经在现实生活中见过这个错误。曾经有一个MMO游戏,它将保持匿名,它有一个bug,允许物品腐烂到-1的价值。如果你把一个-1值的物品放入某些容器中,新值将是4294967296。 :) - JP Alioto

0

在为变量分配值时,C#编译器会在编译时进行自然的隐式转换。但是,只有当它具有足够的上下文来推断所需的数据类型时,才可以应用转换。例如:

uint foo = 1;

这段代码之所以起作用,是因为变量被赋值为已知类型的1。不过

uint foo = condition ? 1 : 2;

无法工作,因为数据类型无法推断。在解析源代码时,编译器将1和2进行比较,以确保它们是相同的类型。由于赋值是一个单独的表达式,它无法从后面的赋值中推断出类型。


0

就实际答案而言,我支持 Pop Catalin 的立场。

如果可以的话,我想补充一下,C++ 中引入的 static_cast<> 操作符产生的二进制文件与类型说明符产生的二进制文件完全一致。也就是说,试着比较和对比使用:

int abc = 123;
uint i = static_cast<unit>(abc); // C++ explicit cast
uint ii = 123U; // specifier
uint j = (uint)abc; // C-style cast

我喜欢显式转换,因为它有助于在回到代码查找微妙的转换错误时。


0

默认情况下,当您输入0、1或任何数字时,它被视为int类型。 由于明显的原因,将int与uint进行比较是不安全的,因为如果您的int小于0会怎样。


0
关于你的C++问题,字面值0默认是有符号的int。表达式~0产生了-1(所有位都为1)的二进制补码表示。所以看起来编译器正在抱怨你试图将一个unsigned int-1进行比较。
话虽如此,我无法在g++ 4.0或4.2上重现你的错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接