0

Possible Duplicate:
Unsigned long with negative value
Assigning negative numbers to an unsigned int?

#include<stdio.h>

int main()
{
    struct a 
    {
        unsigned int i:3;
        int c:3; 
    }s;
    s.i=5;
    s.c=5;

    printf("s.i=%d\n",s.i);
    printf("s.c=%u\n",s.c);
    unsigned int x = -1;
    printf(" x = %d", x);
    return 0;
}

This outputs:

s.i=5
s.c=4294967293
x=-1 

I am not clear about the output of "x" and "s.c"(s.c can store number of 3 bits only but in the output it is giving very large value)."x" is declared as unsigned so the bits stored in x are 1111111....... and the output of x should be a large value instead of -1. 1st printf() statement is giving the result as expected I am using devc++ compiler.

Community
  • 1
  • 1
sourabh912
  • 669
  • 3
  • 7
  • 14

2 Answers2

4

The output depends on the signedness of the format character, not the signedness of the declaration. Think about it: printf can't know whether x was declared int or unsigned int, since no type information is passed in C. So it prints based on how you told it to print. %d is signed, so you get a signed value. With s.c, it's an int so it's signed, but you printed it with %u so it is treated as unsigned.

As for s.i, it is unsigned, so 5 can fit in its 3 bits, so it is passed to printf as 5 without sign extending it, so %d (or %u) prints it as 5.

Jim Balter
  • 16,163
  • 3
  • 43
  • 66
  • if what you said is true then shouldnt the output of printf("s.i=%d\n",s.i); – sourabh912 Jun 24 '12 at 08:07
  • You didn't finish your thought, but I answered in my edit. There's no "if" about it -- this is a basic matter understood by all C programmers who aren't just starting out as you are. – Jim Balter Jun 24 '12 at 08:12
  • sorry there was some problem with my internet connection so couldnt edit my comment.Actually I was asking that shouldnt the output of printf("s.i=%d\n",s.i); be -3 (because my format specifier is %d and "i" is stored in the 3 bits only so 5(101) should be treated as negative). – sourabh912 Jun 24 '12 at 08:23
  • Which, as I said, I answered in my edit. s.i is declared as unsigned int so the sign bit isn't extended, so it doesn't matter whether you print it with %d or %u. Not so with x and s.c, each of which have the sign bit extended, so the value printed depends on the format. – Jim Balter Jun 24 '12 at 08:26
  • @sourabh912 Jim is quite correct here. If you're still having trouble understanding it, perhaps my more long-winded answer will help. Reading the `printf()` manual and the Wikipedia page about two's complement representation may also help. – Darshan Rivka Whittle Jun 24 '12 at 08:34
  • @Darshan I think my answer is lacking in assuming an understanding of how negative integers are represented. Your answer does a good job of remedying that. – Jim Balter Jun 24 '12 at 08:42
2

It seems there are two things going on that need to be understood:

  • printf() conversion specifiers
  • Integral conversions

And two more things that help make sense of your output:

  • Argument promotion for variadic function arguments
  • Two's complement representation

First, printf() is a variadic function. It doesn't know what the types of its arguments are (other than the format string), so you have to use conversion specifiers to tell it how to interpret the arguments. These arguments are subject to the "default argument promotions" such that your 3-bit bit fields are being promoted to ints.

You are using conversions specifiers (%d, %u, and %d) that do not match the signedness of your data, so you will get undefined behavior that depends on how your data is actually represented in memory.

Second, the C11 standard states:

6.3.1.3 Signed and unsigned integers

  • When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.

  • Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.

  • Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

(As far as I can tell, the details relevant here have been true at least since C89.)

This tells us a couple things about your code:

  • When you assign -1 to an unsigned int, UINT_MAX + 1 is added to it, giving UINT_MAX, or 4294967295 for 32-bit integers.

  • When you try to assign 5 to a 3-bit signed bit field, the result is implementation-defined.

So you've got both undefined and implementation-defined behavior, but we can still try to make sense of your output, just for fun. I'm assuming 32-bit integers and two's complement representation.

Your system represents the 4294967295 stored in x as 11111111 11111111 11111111 11111111. When you told printf() that the argument you were passing it was signed, those same bits are interpreted as -1, which is the output you got.

For s.c, the implementation-defined behaviour you seem to have gotten is straightforward: the three bits 101representing 5 got stored as-is. That means that with the correct conversion specifier, printf() should show s.c as -3.

Here are the values you've assigned:

s.i = 101
s.c = 101
  x = 11111111 11111111 11111111 11111111

The 3-bit values are promoted to 32-bit by left-padding with 0 for the unsigned value and repeating the sign for the signed value:

s.i = 00000000 00000000 00000000 00000101
s.c = 11111111 11111111 11111111 11111101
  x = 11111111 11111111 11111111 11111111

Which, when interpreted as signed, unsigned, and signed integers gives:

s.i=5
s.c=4294967293
x=-1

The x=-1 suggests to me that you are in fact using a two's complement representation (which was a pretty safe bet, anyway), and the output for s.c suggests that your ints are 32 bits wide.

Community
  • 1
  • 1
Darshan Rivka Whittle
  • 32,989
  • 7
  • 91
  • 109
  • 1
    Its a very good explanation. I have a small doubt to clear . If the value of s.c=3(011) then it will be converted to s.c = 00000000 00000000 00000000 00000011 as 32 bit integer.Because s.c is signed and its value 5(101) indicates it as a negative number therefore when converted to 32-bit integer there were 1's in all the bit positions except the last three bit positions(in this case as last three bits will be number itself). – sourabh912 Jun 24 '12 at 09:05
  • @sourabh912 Yes, you have it. Note that 111...101 is actually -3. (All of this assumes two's complement arithmetic, which is almost universal.) – Jim Balter Jun 24 '12 at 09:21
  • Thanks Jim and Darshan.Both of you have been a great help. – sourabh912 Jun 24 '12 at 09:41
  • 1
    This answer is incomplete and at least mildly wrong without mentioning undefined behavior. OP's code exhibits bad UB. – R.. GitHub STOP HELPING ICE Jun 24 '12 at 12:22
  • You can't deduce two's complement from the value of `x`. Converting `-1` to an unsigned type always results in `U{type}_MAX`, regardless of which representation is used for signed integers. You can sort-of deduce two's complement from `s.c = 5;` resulting in the value `-3`. Not quite, because converting out-of-range values to signed integer types is implementation-defined, and the standard doesn't prohibit weird methods of doing it. – Daniel Fischer Jun 24 '12 at 15:47
  • @ R.. Can you please complete the answer – sourabh912 Jun 24 '12 at 15:49
  • @R Thanks for the comment. I've updated my answer -- please let me know if you happen to notice any other mistakes. – Darshan Rivka Whittle Jun 24 '12 at 21:31
  • @DanielFischer First, thanks for the comment. Now that I understand better how signed-to-unsigned conversion works, I think the proof (or close to it) of two's complement is that that `UINT_MAX` is output as `-1` when you tell `printf()` it's a signed value. Anyway, thanks for the corrections, and please let me know if you notice anything else astray. – Darshan Rivka Whittle Jun 24 '12 at 21:34
  • 1
    That's a great edit, +1. Printing `UINT_MAX` as `-1` with `%d` is indeed a very strong indication of two's complement (but of course not a proof, since it's undefined behaviour - as close to a proof as ou can get, though). – Daniel Fischer Jun 24 '12 at 21:47