views:

483

answers:

7

I have run into a couple of similar quirks regarding uint usage in both C++ and C#, and now I'm wondering on the reasoning (which may be completely different for each example). For both of these examples, note that I am compiling with the warning levels set to maximum.

(1) gcc complains about comparing an int to a uint in the following, whereas vc++ does not:

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

Comparing to 0 is just fine without any casting on both gcc and vc++.

(2) In C# 3.5, I just ran into a similiar issue. The following works fine:

uint foo = 1;
uint bar = 2;

But this gives a uint/int warning:

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

What gives, why is the compiler so sensitive about signed-ness of immediate values? I completely understand the issue when assigning from variables, but this just doesn't make sense to me for immediate values; is there some hidden difficulty in the parsing that prevents this behavior from being allowed? Or what?

Edit: Yes, I know I can suffix my numbers with 'u', but that sidesteps my question, which is about implicitly casting to the left-hand-side, not explicitly casting the right-hand-side.

+3  A: 

I can't speak for gcc but as for the C# 3 compiler you need to explicitly tell it that these ints ought to be unsigned:

uint foo = condition ? 1U : 2U;

The C# compiler loves ints and assumes all integral values within range are ints. Since your expression is using a conditional operator the compiler is too eager to assume your literal values are ints and then the assignment fails.


Edit: Note that I said integral values that are within range of a System.Int32. Consider this example:

using System;    

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

Output:

System.Int32
System.UInt32

Andrew Hare
So you're saying the C# compiler is assigning the type to an immediate as soon as it is seen, instead of resolving the ambiguity up the parse tree when the immediate is actually read from?
Not Sure
@Not Sure: That's correct, C# will try to do any implicit casts but numeric literals are always int32.
Pop Catalin
@Pop Catalin: Please see my edit - you comment is not completely true :)
Andrew Hare
@Andrew: you're right, I completely forgot about that, even though I've used long literals without suffix before :s ...
Pop Catalin
@Pop Catalin: No worries - I was just nitpicking :)
Andrew Hare
A: 

By deafult when you type 0, or 1 or any number its considered an int. comparing int with and uint is not safe for obvious reasons, that what if your int is less than 0.

Yogi
+1  A: 

Let me ask you a question ... assume you have a big endian, 32-bit machine that represents numbers in 2's complement. What unsigned int value is equal to the signed int value of 0xFFFFFFFF? Okay, so wouldn't you warn if you saw somebody doing that?

JP Alioto
I like the brevity of your approach. (+1)What about positive numbers? Couldn't the compiler check them and just go on?
GregC
Not really. The problem just moves to the other end. There is no positive signed int that is equal to anything higher than unsigned 0x0FFFFFFF (32-bit, 2's complement). So the question is as a compiler, should I warn the user that they may be making a mistake? I've actually seen this bug in real life. There once was an MMO that will remain nameless that had a bug where it would allow an item to decay to a value of -1. If you put a -1 value item into some containers, the new value would be 4294967296. :)
JP Alioto
+7  A: 

Mixing signed and unsigned values without explicit programmer intent can lead to subtle bugs. It's true that both int and uint are stored inside memory locations of the same size (4 bytes) and are location assignment compatible, but their bit representation and behavior in regards to common operations is different, and they also have different ranges.

It's like solving a math problem and saying why can't I freely exchange the [-2147483648 to 2147483647] interval with a [0 to 4294967295] interval? Well you can, but if you go out of bounds the results might not be the correct ones :). That's why the compiler asks for your confirmation (by being explicit), that you're not mixing different types by mistake.

Also in C# literal numbers are always int32, if you want some other literal type like float, decimal, ulong etc. you need to use the appropriate suffix, in your case:

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

Edit: As Andrew Hare points, C# integer literals are not int32 only, but (int, uint, long, ulong) depending on size, as described here:

C# Inger literals - MSDN

Pop Catalin
+1 Very nice explanation!
Andrew Hare
A: 

When assigning values to variables, the C# compiler does natural implicit conversion at compile time. However it can only apply the conversion if it has enough context to infer the desired data type. For example:

uint foo = 1;

...works because the value 1 is assigned to a variable of known type. However

uint foo = condition ? 1 : 2;

doesn't work because the data type cannot be inferred. When parsing the source the compiler compares 1 and 2 with each other to make sure they are of the same type. It can't infer from the assignment that follows since it is a separate expression.

Paul Alexander
A: 

As to your C++ question, the literal 0 is, by default, a signed int. The expression ~0 produces the twos-compliment representation of -1 (all bits on). So it looks like the compiler is complaining that you are trying to compare an unsigned int to -1.

That said, I was unable to reproduce your error on g++ 4.0 or 4.2.

Naaff
A: 

I side with Pop Catalin as far as the actual answer goes.

I'd like to add, if i may, that static_cast<> operator introduced in C++ results in a binary that's just like the one produced by a type specifier. That is, try to compare and contrast usage:

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

I like the explicit cast, because it helps when you come back to the code looking for subtle casting bugs.

GregC