Reputation: 1569
In the following example the bit representation of byte with all ones is printed:
#include <stdio.h>
int main (void)
{
char c = 255;
char z;
for (int i = 7; i >= 0; i--) {
z = 1 << i;
if ((z & c) == z) printf("1"); else printf("0");
}
printf("\n");
return 0;
}
The output is 11111111
Now we change char c
to int c
, so that the example becomes:
#include <stdio.h>
int main (void)
{
int c = 255;
char z;
for (int i = 7; i >= 0; i--) {
z = 1 << i;
if ((z & c) == z) printf("1"); else printf("0");
}
printf("\n");
return 0;
}
Now the output is 01111111
.
Why the output is different?
UPDATE
Compile the following test.c
:
#include <stdio.h>
int main(void)
{
char c=-1;
printf("%c",c);
return 0;
}
$ gcc test.c
$ ./a.out | od -b
0000000 377
0000001
The output is 377, which means that glibc contradicts to gcc, because signed char is converted to unsigned char automatically. Why such complications? It is reasonable to have char unsigned by default. Is there any specific reason why not?
Upvotes: 2
Views: 394
Reputation: 213832
The first problem here is the char
type. This type should never be used for storing integer values, because it has implementation-defined signedness. This means that it could be either signed or unsigned, and you will get different results on different compilers. If char
is unsigned on the given compiler, then this code will behave as you expected.
But in case char
is signed, char c = 255;
will result in a value which is too large. The value 255 will then get converted to a signed number in some compiler-specific way. Usually by translating the raw data value to the two's complement equivalent.
Good compilers like GCC will give a warning for this: "overflow in implicit constant conversion".
Solve this bug by never using char
for storing integers. Use uint8_t
instead.
The same problem appears when you try to store 1 << 7
inside a char
type that is signed on your given compiler. z
will end up as a negative value (-128) when that happens.
In the expression z & c
, both operands are silently integer promoted to type int
. This happens in most C expressions whenever you use small integer types such as char
.
The &
operator doesn't care if the operands are signed or not, it will do a bitwise AND on the "raw data" values of the variables. When c
is a signed char
and has the raw value 0xFF
, you will get a result which is negative, with the sign bit set. Value -1
on two's complement computers.
So to answer why you get different results in the two cases:
When you switch type to int
, the value 255
will fit inside c
without getting converted to a negative value. The result of the &
operation will also be an int
and the sign bit of this int
will never be set, unlike it was in the char
case.
When you execute -128 & 255
the result will be 128
(0x80). This is a positive integer. z
is however a negative integer with the value -128
. It will get promoted to int
by the == operator but the sign is preserved. Since 128 is not equal to -128, the MSB will get printed as a zero.
You would get the same result if you switched char
to uint8_t
.
Upvotes: 1
Reputation: 28830
(edit to clarify "signed by default")
In the first listing, (z == c)
tests two char ; in the second listing however, (z == c)
tests one char and one int.
In order to perform the &
and ==
operations between a char and an int the compiler expands the char to the size of an int.
Regarding the bit 7 (8th):
If your compiler would consider char to be unsigned by default, the condition
(((int)(128) & (int)255) == (int)128)
would render true, and a 1
would be printed. However in your case the result is false, and a 0
is displayed.
The reason is likely your compiler that considers char to be signed (like gcc by default). In this case, a char set to 1 << 7
is actually -128
, while in an int (at least two bytes) 255 is positive.
(char)-128
expanded to an int is (int)-128
, thus the condition
if ((z & c) == z)
reads
if (((int)(-128) & (int)255) == (int)-128)
which is false in this case.
Upvotes: 0
Reputation: 1836
for char to int, you have to define char as unsigned because by default char or any type is treated as singed.
int main (void)
{
int c = 255;
unsigned char z;
int i;
for (i = 7; i >= 0; i--) {
z = 1 << i;
if ((z & c) == z) printf("1"); else printf("0");
}
printf("\n");
return 0;
}
Upvotes: 0