Why doesn't my mini-mini-construct work in C
What I'm trying to do is make a mask from 1 bit to the end of the left side of the bit set, and the rest is zero, regardless of the size of the variable. I tried the following:
unsigned char x = ~(~0 >> 1);
which, for me, should work, whether on char or int, but it doesn't!
For me, the manipulation looks like this:
||||||||
0|||||||
|0000000
This is what appears to be the same as a 16-bit integer:
|||||||| ||||||||
0||||||| ||||||||
|0000000 00000000
Why doesn't this design work? This gives me zero whether I am trying to assign it to an unsigned char or int.
I am on 50 pages of K&R so I am fairly new. I don't know what literal means, I'm not sure what an "arithmetic" shift is, I don't know how to use the suffix "and I'm pretty damn sure I can't use a structure.
source to share
~0
is a zero int
with all bits reversed, which is int
, consisting of all. On a 2s add-on machine, this is -1
. Right-offsetting a -1
will cause sign expansion, so that's ~0 >> 1
all the same.
What you want is a right offset of the quantity unsigned
, which will not cause sign expansion.
~0u >> 1
is an unsigned integer with a high-order zero bit and all others are set to 1, so
~(0u >> 1)
is an unsigned integer with the high order bit of one and all others set to zero.
Now getting this to work for all data sizes is non-trivial since C converts the operands of integer arithmetic to int
or unsigned int
beforehand. For example,
~(unsigned char)0 >> 1
produces the int
result -1
because the unsigned char is "promoted" int
before being applied ~
.
So, to get what you want with all data types, the only way I can see is to use sizeof
to find out how many bytes (or octets) there are in the data.
#include <stdio.h>
#include <limits.h>
#define LEADING_ONE(X) (1 << (CHAR_BIT * sizeof(X) - 1))
int main(void) {
printf("%x\n", LEADING_ONE(char));
printf("%x\n", LEADING_ONE(int));
return 0;
}
source to share
The general rule for C is that expressions are evaluated on a generic type, in this case (sign) an integer. The score (~ 0) and (~ 0 → 1) are whole integers, and the shift is an arithmetic shift. In your case, this is done with sign expansion, so:
(0xffffffff >> 1) => (0xffffffff)
The logical shift will introduce zero from the left, which you expected, so your problem is how to get the compiler to do the logical shift. Try:
unsigned char a = ~0;
unsigned char b = a >> 1; // this should do a logical shift
unsigned char c = ~b;
There are better ways to do what you are trying, but this should help you fix your current problem.
source to share