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

我在C ++和C#中遇到了关于uint使用的几个类似的怪癖,现在我想知道推理(对于每个例子可能完全不同)。 对于这两个示例,请注意我正在编译时将警告级别设置为最大值。

(1)gcc抱怨在下面将int与uint进行比较,而vc ++没有:

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

比较0就好了,没有在gcc和vc ++上进行任何转换。

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

 uint foo = 1; uint bar = 2; 

但这会给出一个uint / int警告:

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

是什么给出了,为什么编译器对立即值的签名如此敏感? 我完全理解从变量赋值时的问题,但这对我来说对于直接值是没有意义的; 在解析中是否存在一些隐藏的难以阻止此行为被允许? 或者是什么?

编辑:是的,我知道我可以用’你’来填充我的数字,但是回避了我的问题,这是关于隐含地向左侧施放,而不是明确地施放右侧。

我不能说gcc但对于C#3编译器,你需要明确告诉它这些ints应该是无符号的:

 uint foo = condition ? 1U : 2U; 

C#编译器喜欢 ints并假设范围内的所有整数值都是ints 。 由于您的表达式使用条件运算符,编译器太急于假设您的文字值是ints ,然后分配失败。


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

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

输出:

System.Int32
System.UInt32

在没有明确的程序员意图的情况下混合有符号和无符号值可能会导致细微的错误。 确实,int和uint都存储在相同大小(4字节)的内存位置中并且位置分配兼容,但它们在常见操作方面的位表示和行为是不同的,并且它们也具有不同的范围。

这就像解决数学问题并说为什么我不能以[0到4294967295]间隔自由交换[-2147483648到2147483647]间隔? 嗯,你可以,但如果你超出界限,结果可能不是正确的:)。 这就是为什么编译器要求你确认(通过明确),你没有错误地混合不同的类型。

同样在C#中,字面数字总是int32,如果你想要一些其他文字类型,如float,decimal,ulong等,你需要使用适当的后缀,在你的情况下:

 uint foo = condition ? 1u : 2u; // uint literals; 

编辑:正如Andrew Hare指出的那样,C#整数文字不仅仅是int32,而是(int,uint,long,ulong),具体取决于大小,如下所述:

C#Inger文字 – MSDN

让我问你一个问题……假设你有一个大端,32位机器代表2的补码中的数字。 unsigned int值等于signed VM值0xFFFFFFFF? 好吧,如果你看到有人这样做,你也不会警告吗?

当你输入0,或1或任何数字时,通过聋,它被认为是一个int。 比较int和uint是不安全的,原因很明显,如果你的int小于0怎么办。

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

 uint foo = 1; 

…有效,因为值1被分配给已知类型的变量。 然而

 uint foo = condition ? 1 : 2; 

不起作用,因为无法推断数据类型。 解析源时,编译器会将1和2相互比较,以确保它们属于同一类型。 它不能从后面的赋值中推断出来,因为它是一个单独的表达式。

至于你的C ++问题,默认情况下,文字0是带符号的int 。 表达式〜0产生-1的二进制补码表示(所有位都打开)。 所以看起来编译器抱怨你试图将unsigned int-1进行比较。

也就是说,我无法在g ++ 4.0或4.2上重现您的错误。

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

我想补充说,如果可以的话,在C ++中引入的static_cast <>运算符会产生一个二进制文件,就像类型说明符生成的二进制文件一样。 也就是说,尝试比较和对比使用:

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

我喜欢显式强制转换,因为当你回到代码中寻找微妙的强制转换错误时,它会有所帮助。