Why Avoid Comparison of signed value with unsigned value in C

Discussion in 'C' started by poornaMoksha, Nov 27, 2011.

  1. poornaMoksha

    poornaMoksha New Member

    Joined:
    Jan 29, 2011
    Messages:
    150
    Likes Received:
    33
    Trophy Points:
    0
    Occupation:
    Software developer
    Location:
    India
    If you work 3-4 hours daily on coding in C/C++ then there ought to be some programs, logics, tricks, tips etc which you come across daily. Some are new to only you while others are interesting enough to be shared. Here in this article, I am going to share one interesting program and how I went about debugging it. I hope every visitor of this article will enjoy.

    The for loop



    Can you tell me what would be the output of the following piece of code??

    Code:
    #include"stdio.h" 
    #include"stdlib.h" 
    #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0])) 
    int array[] = {23,34,12,17,204,99,16}; 
    int main() 
    { 
    int d; 
    for(d=-1;d <= (TOTAL_ELEMENTS-2);d++) 
    printf("%d\n",array[d+1]); 
     
    return 0; 
    }
    Any Idea??

    Ok, lets run it :

    Code:
    ~/practice $ gcc -Wall for.c -o for 
    ~/practice $ ./for 
    ~/practice $
    What!!!!...Yes, You saw it correct. There was no output. Now, since you know the output, can you tell me the reason??

    Any Ideas??

    Ok, I'll tell you what I did. I tried to scrutinize the code and find any doubtful piece(if present). The only thing that looked odd to me was the initialization of variable 'd' with -1. I mean why would someone do that?? Mostly we initialize for loop iterator variable with '0', then why not here. So i thought to repair this first and then continue my investigation further and look what I saw :

    Code:
    #include"stdio.h" 
    #include"stdlib.h" 
    #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0])) 
    int array[] = {23,34,12,17,204,99,16}; 
    int main() 
    { 
    int d; 
    for(d=0;d <= (TOTAL_ELEMENTS-1);d++) 
    printf("%d\n",array[d+1]); 
     
    return 0; 
    }
    I compiled and executed the above code and to my suprise I got :

    Code:
    ~/practice $ ./for 
    34 
    12 
    17 
    204 
    99 
    16 
    0
    Wow...yes that was my reaction on it. Does that mean for loop does not execute when the iterator variable has negative value. Well, I think so...but the big question WHY?? was still lingering around...

    After researching a bit, I found something which I believed may lead up to the reason of the above problem.

    Before opening all the cards, lets study a concept.

    Signed Vs unsigned values



    I know most of you must be thinking about whats new in this..its a trivial concept. Right??? But still, lets glance this through at least.

    A signed variable can hold a signed value. A signed value can be positive or negative. The range may depend upon the type of variable holding the value.
    An unsigned value is always a positive value(including '0'). The range of unsigned values depends upon the unsigned variable type being used.

    For example :
    int a = -1; // signed value being stored in signed integer.
    unsigned int b = 0xFFFFFFFA // huge unsigned value being stored in unsigned integer

    Unsigned is fine, but have you ever thought how signed values ie negative values are stored in memory? How is a negative value read?.

    For those who have been from electronics background(like me :) ) would have studied the concept of two's complement. A negative number is stored in memory as a two's or 2's complement of its positive counterpart.

    Lets suppose we have a positive number 28. It is represented in 64 bit binary as :

    00000000000000000000000000011100

    Now, to find its two's component the first step is to invert all the bits ie replace '1' by '0' and '0' by '1'.

    So now the binary representation becomes :

    11111111111111111111111111100011

    Now, the second step is to add '1' to this binary number ie :

    11111111111111111111111111100011
    + 1

    So the result comes out to be : 11111111111111111111111111100100 ie ffffffe4

    Some of the newbies would be amazed to know that the above result is -28. For those who do not believe, lets prove it :

    Code:
    #include<stdio.h> 
     
    int main(void) 
    { 
        printf("\n %x \n", (int)-28); 
        return 0; 
    }
    The output of the following code comes out to be : ffffffe4.

    So, by now all your doubts regarding negative value representation would have been cleared.

    Coming back to the original problem



    So coming back to my original doubt, which I did not disclose earlier.

    Since for loop is not executing at all, So it technically means that the comparison condition is failing. ie when d = -1, then somehow d <= (TOTAL_ELEMENTS-2) is not true. This is how I researched a bit on this assumption based on the above built understanding.
    • Firstly, I found what -1 would look like in memory. It would look something like 0xffffffff (you can check it for yourself now)
    • Now, how would TOTAL_ELEMENTS-2 look like in memory. It would look something like : 0x5
    • Now I studied a bit on how comparison takes place in C, gcc. I looks like the larger data type (having larger capacity)of the two is selected and both the values which are being compared are upgraded into temporary variables of this selected type.
    • Now since in our case d is of type int while (TOTAL_ELEMENTS-2) is of type unsigned int (default in case of constants and #defines), so both the values are upgraded and stored into temporary variables of unsigned int type.
    • So now when you compare 0xffffffff and 0x5 at a common platform of unsigned int. Then obviously 0xffffffff is far bigger and hence the condition is definitely false at the first iteration itself and hence for loop does not execute at all.
    My goodness!!!! that wasn't trivial at all. What do you say. Okay, Thats why this is written all over in our text books in schools and colleges that never compare signed and unsigned values. I hope now you all are also clear of it.

    But one last thing, I wanted to be double sure of my understanding, so I decided to change the code a bit and balance out the comparison in the code itself.

    So, this is what I did :

    Code:
    #include"stdio.h" 
    #include"stdlib.h" 
    #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0])) 
    int array[] = {23,34,12,17,204,99,16}; 
    int main() 
    { 
    int d; 
    for(d=-1;d <= (int)(TOTAL_ELEMENTS-2);d++) 
    printf("%d\n",array[d+1]); 
    //system("pause"); 
    return 0; 
    }
    So, i brought the constant (TOTAL_ELEMENTS-2) to the level of 'd' ie made the constant as signed integer. Now since both are of same type so no up-gradation or degradation of types will take place and both will be compared as signed values.

    Now when I executed the code, I got :

    Code:
     ~/practice $ ./for 
    23 
    34 
    12 
    17 
    204 
    99 
    16
    Wooohooo....I was correct..the output is correct this time.

    Conclusion



    To conclude, one must be very much cautious of each and every line a language specification says as you never know when and in which form you may encounter that rule and leave you scratching your head.

    Stay tuned for more!!!
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice