2

I've been writing a program in C to move the first 4 bits of a char to the end and the last 4 to the start. For most values it works normally, as well as the reverse operation, but for some values, as 8, x, y, z, it gives as result a 32 bit value. Values checked through printing hex value of the variable. Can anybody explain why this is happening?

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char o, f,a=15;
    scanf("%c",&o);
    printf("o= %d\n",o);
    f=o&a;
    o=o>>4;
    printf("o= %d",o);
    o=o|(f<<4);
    printf("o= %x, size=%d\n",o,sizeof(o));
    f=o&a;
    o=o>>4;
    printf("o= %d",o);
    o=o|(f<<4);
    printf("o= %x, size=%d\n",o,sizeof(o));
    return 0;
}
Alex Chamberlain
  • 4,147
  • 2
  • 22
  • 49
Dumitru
  • 771
  • 2
  • 12
  • 23
  • `sizeof(o)` is one of those things that will never change – K-ballo Jun 12 '12 at 17:37
  • @K-ballo however it's good practice to be consistent; all other sizes require this operator, so the type of `o` ever changes, it won't give you a hard time. –  Jun 12 '12 at 17:44
  • @H2CO3: I got the impression the OP is printing the result of `sizeof(o)` to see if the type, and hence the size, of `o` changed. – K-ballo Jun 12 '12 at 17:47
  • Yes, but I don't see why that's related to my comment. He obviously needs to observe the size if he prints it, and he maybe won't use this code with chars only, but possibly with other data types. –  Jun 12 '12 at 17:49
  • @H2CO3: C is statically typed. Printing `sizeof(o)` twice in the same scope is redundant, regardless of what the code is used for. – Wooble Jun 12 '12 at 19:23
  • You're right: I didn't say it was not redundant. I'm saying he prints it because he needs it. (It's obviously a stupid idea to print sizeof(char), but anyway, he wants to be sure or whatever...) –  Jun 12 '12 at 19:39
  • @H2CO3: yeah, it is stupid, but after weird results it just made sense to check. Afterwards I just copied the code here without checking. It's just a test code, nothing serious. – Dumitru Jun 12 '12 at 19:49

4 Answers4

6

Passing o as an argument to printf() results in an automatic conversion to int, which is apparently 32-bit on your system. The automatic conversion uses sign-extension, so if bit 7 in o is set, bits 8-31 in the converted result will be set, which will explain what you are seeing.

You could use unsigned char instead of char to avoid this. Or pass o & 0xff to printf().

twalberg
  • 59,951
  • 11
  • 89
  • 84
3

Try declaring your variables as unsigned char. You are getting a sign-extension of the upper bit.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • Sorry for not mentioning this in the question, but I already know how to fix it. What I'm really asking for is why this is happening. Thanks for the answer, though. – Dumitru Jun 12 '12 at 17:45
2

A char given as argument to printf (or any variadic function, or any function without prototype) is promoted to int. If char is signed on your platform and the passed value is negative, the sign is extended.

AProgrammer
  • 51,233
  • 8
  • 91
  • 143
2

Your values are printed as 32-bit values because your format specifiers %x tell printf to print the value as an unsigned int. To print it as an unsigned char, you need the format specifier %hhx with the hh length modifier. If the values are positive, that makes no difference for the printed output, but for negative numbers it does because they have the most significant bit set.

For the following, explaining how negative values arise in that code, I assume CHAR_BIT == 8 and twos complement representation for negative integers.

For the shifts, the value of o is promoted to int. If the fourth least significant bit of the original value was set (if (o & 8) != 0), after the first swap of the nibbles, the most significant bit of o is set. If char is by default signed on your platform, that means the result is negative. For the second nibble-swap, the value of o is again promoted to int, resulting in a negative value. The right-shifting of negative values is implementation-defined, so

o=o>>4;

is not portable in that case (although, in practice all implementations use either an arithmetic right shift [with sign-extension] or a logical right shift [shifting in zeros from the left]).

On implementations doing an arithmetic shift on negative integers, the four most significant bits of o will all be set, so

o=o|(f<<4);

doesn't change the value anymore.

The only way to portably fix the code to obtain the desired behaviour is to declare o as an unsigned char as suggested by Ned. Then all values are positive and the behaviour of the shifts is well-defined and matches your expectations.

Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431