78

int:

The 32-bit int data type can hold integer values in the range of −2,147,483,648 to 2,147,483,647. You may also refer to this data type as signed int or signed.

unsigned int :

The 32-bit unsigned int data type can hold integer values in the range of 0 to 4,294,967,295. You may also refer to this data type simply as unsigned.

Ok, but, in practice:

int x = 0xFFFFFFFF;
unsigned int y = 0xFFFFFFFF;
printf("%d, %d, %u, %u", x, y, x, y);
// -1, -1, 4294967295, 4294967295

no difference, O.o. I'm a bit confused.

Taryn East
  • 27,486
  • 9
  • 86
  • 108
Fabricio
  • 7,705
  • 9
  • 52
  • 87
  • 1
    You need to think about the _binary_ representation of both an `int` and an `unsigned int`. – Oded Jan 28 '12 at 13:08
  • 3
    The real reason that this can happen is that C is a *weakly* *typed* language. But `unsigned int` and `int` are really different. – cha0site Jan 28 '12 at 13:13
  • 1
    http://stackoverflow.com/questions/247873/signed-versus-unsigned-integers – Rafał Rawicki Jan 28 '12 at 13:18
  • possible duplicate of [why unsigned int 0xFFFFFFFF is equal to int -1?](http://stackoverflow.com/questions/1863153/why-unsigned-int-0xffffffff-is-equal-to-int-1) – Rafał Rawicki Jan 28 '12 at 13:18

9 Answers9

57

Hehe. You have an implicit cast here, because you're telling printf what type to expect.

Try this on for size instead:

unsigned int x = 0xFFFFFFFF;
int y = 0xFFFFFFFF;

if (x < 0)
    printf("one\n");
else
    printf("two\n");
if (y < 0)
    printf("three\n");
else
    printf("four\n");
cha0site
  • 10,517
  • 3
  • 33
  • 51
32

Yes, because in your case they use the same representation.

The bit pattern 0xFFFFFFFF happens to look like -1 when interpreted as a 32b signed integer and as 4294967295 when interpreted as a 32b unsigned integer.

It's the same as char c = 65. If you interpret it as a signed integer, it's 65. If you interpret it as a character it's a.


As R and pmg point out, technically it's undefined behavior to pass arguments that don't match the format specifiers. So the program could do anything (from printing random values to crashing, to printing the "right" thing, etc).

The standard points it out in 7.19.6.1-9

If a conversion specification is invalid, the behavior is undefined. If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.

cnicutar
  • 178,505
  • 25
  • 365
  • 392
  • 4
    The real reason is that he is using %u to print both. – shadyabhi Jan 28 '12 at 13:11
  • 5
    The real reason is that he **invoked undefined behavior** by passing a non-type-matching format string to `printf`. I almost want to downvote all the answers that are skipping this and pointing out irrelevant stuff about the representation... – R.. GitHub STOP HELPING ICE Jan 28 '12 at 13:33
  • 1
    @R.. That's right, mismatching the format and the arguments is undefined behavior. Feel free to downvote, I won't mind :-) – cnicutar Jan 28 '12 at 13:33
  • @R..: You're *wrong*, this is not undefined behaviour. See C99 section 6.5.2.2 paragraph 6. – cha0site Jan 28 '12 at 13:49
  • @cha0site No, he's right, read my updated answer (specifically the quote from the draft). – cnicutar Jan 28 '12 at 13:55
  • @cnicutar: C99 [6.5.2.2](http://port70.net/~nsz/c/c99/n1256.html#6.5.2.2), especially the bit about integer promotion. – cha0site Jan 28 '12 at 14:03
  • 1
    The documentation of `fprintf` is very explicit: "If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined." – R.. GitHub STOP HELPING ICE Jan 28 '12 at 22:05
  • "If the function is defined with a type that does not include a prototype, and the types of the arguments after promotion are not compatible with those of the parameters after promotion, the behavior is undefined, except for the following cases: one promoted type is a signed integer type, the other promoted type is the corresponding unsigned integer type, and the value is representable in both types; both types are pointers to qualified or unqualified versions of a character type or void." – Antti Haapala -- Слава Україні Mar 01 '17 at 10:51
  • The value `-1` is not representable in unsigned int. – Antti Haapala -- Слава Україні Mar 01 '17 at 10:51
23

There is no difference between the two in how they are stored in memory and registers, there is no signed and unsigned version of int registers there is no signed info stored with the int, the difference only becomes relevant when you perform maths operations, there are signed and unsigned version of the maths ops built into the CPU and the signedness tell the compiler which version to use.

Nathan Day
  • 5,981
  • 2
  • 24
  • 40
6

The problem is that you invoked Undefined Behaviour.


When you invoke UB anything can happen.

The assignments are ok; there is an implicit conversion in the first line

int x = 0xFFFFFFFF;
unsigned int y = 0xFFFFFFFF;

However, the call to printf, is not ok

printf("%d, %d, %u, %u", x, y, x, y);

It is UB to mismatch the % specifier and the type of the argument.
In your case you specify 2 ints and 2 unsigned ints in this order by provide 1 int, 1 unsigned int, 1 int, and 1 unsigned int.


Don't do UB!

pmg
  • 106,608
  • 13
  • 126
  • 198
  • 4
    -1, wrong. This is not undefined behaviour. Check out C99 section 6.5.2.2 paragraph 6. – cha0site Jan 28 '12 at 13:47
  • 1
    @cha0site: I specifically said the assignments are ok: *section 6.5.2.2 paragraph 6* does not really apply to my answer. The UB is in the `printf` call: check [7.19.6.1](http://port70.net/~nsz/c/c99/n1256.html#7.19.6): "If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined". Also notice `printf` is a function taking a variable number of arguments and the conversions according to the prototype are not possible. – pmg Jan 28 '12 at 13:55
  • C99 [6.5.2.2](http://port70.net/~nsz/c/c99/n1256.html#6.5.2.2) takes precedence over that. There's integer promotion afoot. I may have miscounted - it's possibly paragraph 5. – cha0site Jan 28 '12 at 14:00
  • @cha0site No it doesn't. Arguments can't be promoted to anything when the function takes a variable number of parameters. – cnicutar Jan 28 '12 at 14:08
  • 1
    @cnicutar: What? Of course they can. That's exactly what it says there: "If the function is defined with a type that does not include a prototype, and the types of the arguments after promotion are not compatible with those of the parameters after promotion, the behavior is undefined, except for the following cases: one promoted type is a signed integer type, the other promoted type is the corresponding unsigned integer type, and the value is representable in both types;" – cha0site Jan 28 '12 at 14:11
  • @cha0site I don't see anything about `printf` which is a function defined **with a prototype**. – cnicutar Jan 28 '12 at 14:15
  • @cha0site: Even in the case of a function without a prototype, passing the wrong signedness argument will result in UB. Default promotions only promote smaller-than-int types to int. They do not fix signedness mismatches. There's a special requirement by the standard that values representable in both the corresponding signed and unsigned type have the same representation in each and that, as long as the value fits in both, passing a value of the wrong signedness does not result in UB. But in OP's example, the whole issue is that the value does not fit in both types! – R.. GitHub STOP HELPING ICE Jan 28 '12 at 22:08
5

The binary representation is the key. An Example: Unsigned int in HEX

 0XFFFFFFF = translates to = 1111 1111 1111 1111 1111 1111 1111 1111 

Which represents 4,294,967,295 in a base-ten positive number. But we also need a way to represent negative numbers. So the brains decided on twos complement. In short, they took the leftmost bit and decided that when it is a 1 (followed by at least one other bit set to one) the number will be negative. And the leftmost bit is set to 0 the number is positive. Now let's look at what happens

0000 0000 0000 0000 0000 0000 0000 0011 = 3

Adding to the number we finally reach.

0111 1111 1111 1111 1111 1111 1111 1111 = 2,147,483,645

the highest positive number with a signed integer. Let's add 1 more bit (binary addition carries the overflow to the left, in this case, all bits are set to one, so we land on the leftmost bit)

1111 1111 1111 1111 1111 1111 1111 1111 = -1

So I guess in short we could say the difference is the one allows for negative numbers the other does not. Because of the sign bit or leftmost bit or most significant bit.

pgreinhard
  • 67
  • 1
  • 4
4

The internal representation of int and unsigned int is the same.

Therefore, when you pass the same format string to printf it will be printed as the same.

However, there are differences when you compare them. Consider:

int x = 0x7FFFFFFF;
int y = 0xFFFFFFFF;
x < y // false
x > y // true
(unsigned int) x < (unsigned int y) // true
(unsigned int) x > (unsigned int y) // false

This can be also a caveat, because when comparing signed and unsigned integer one of them will be implicitly casted to match the types.

Rafał Rawicki
  • 22,324
  • 5
  • 59
  • 79
4

He is asking about the real difference. When you are talking about undefined behavior you are on the level of guarantee provided by language specification - it's far from reality. To understand the real difference please check this snippet (of course this is UB but it's perfectly defined on your favorite compiler):

#include <stdio.h>

int main()
{
    int i1 = ~0;
    int i2 = i1 >> 1;
    unsigned u1 = ~0;
    unsigned u2 = u1 >> 1;
    printf("int         : %X -> %X\n", i1, i2);
    printf("unsigned int: %X -> %X\n", u1, u2);
}
AlexT
  • 1,413
  • 11
  • 11
2

The type just tells you what the bit pattern is supposed to represent. The bits are only what you make of them. The same sequences can be interpreted in different ways.

maiwald
  • 891
  • 12
  • 26
1

The printf function interprets the value that you pass it according to the format specifier in a matching position. If you tell printf that you pass an int, but pass unsigned instead, printf would re-interpret one as the other, and print the results that you see.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523