Question

I'm trying to figure out how does this code work, but I can't manage to get a single answer.

#define testbit(x, y)  ( ( ((const char*) & (x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )

I'm new at pointers, so if you can figure out a way to explain this in simplified english, I would really appreciate it.

It belongs to a segment of code for an X-Plane Plug-in found at https://code.google.com/p/xplugins/source/browse/trunk/Xsaitekpanels/SwitchPanel.cpp?r=38 line=19

Was it helpful?

Solution

The macro tests the value of the y-th bit in x. You can't directly address bits, so the code starts by treating x as an array of bytes (the const char* cast).

It then looks up the byte where the bit lives. There are 8 bits in a byte, so it divides by 8. Chasing performance, instead of simply dividing by 8, the code uses the binary trick of shifting right 3 places. In general, for unsigned x and y, x >> y = x/2^y, and x << y = x*2^y.

At this point you need to test the bit within the byte, so you get the remainder of y/8. Yet another bit trick, using y & 7 instead of the clearer y % 8.

With this information you can make a mask, a single on bit, 0x80 and shift it into position to test the y%8-th bit. The mask is ANDed against the byte and a non-zero result here means the bit was set to 1, otherwise 0.

OTHER TIPS

Completing @RhythmicFistman's answer

@RhythmicFistman's answer is missing one small part to it and that is the last step in the shifts.

The >> (7-((y)&0x07) step ensures that you only ever get a result of 1 or 0. With this code it is safe to do comparisons like:

if (testbit(varible, 6) == 1) {
    // do something
}

Where without that step testbit would return a bit mask in which the 6th bit would be set to 1 or 0 and all the other bits are always set to 0. That is the intent but it is not implemented in what is considered a portable way, see Warning 3 below.

Possible issues with using this code

Now to add something to the other answers. The other answers have not pointed out some keywords that should be mentioned here and they are strict aliasing and shift arithmetic right. My elaboration will come in the form of warnings below.

Warning 1: Endianness

This code assumes that you are using a big endian architecture or only wish to get the correct bit from an array of chars.

The reason is that if you convert an int into an array of chars (bytes) you will get different results on a big endian machine vs a little endian machine.

Warning 2: Strict Aliasing

The macro makes use of a cast (const char*) &(x) which is designed to change the type, a.k.a. alias, of (x) so that it is easier to get to the correct bits.

This is dangerous and the reason why is explained beautifully in this SO answer. The short version is that if you compile this code with optimisations strange things can happen.

The wikipedia pages on Aliasing and Pointer Aliasing are also useful and should be read.

Warning 3: Shift Arithmetic Right

In addition to this there could be a potential issue with the way this code uses the right shift operator >>. This operator has two different behaviors depending on whether the variable it is operating on is signed or unsigned. So long as you never use negative numbers you will be safe but this code will not protect you against that mistake. I suspect though, that you're less likely to make such a mistake anyway so it should be ok to use it.

Also worth mentioning, you are using signed char and are shifting it right. Though this works I would prefer unsigned char which would improve portability because it will not risk generating an arithmetic shift right when char and int are the same width (which is almost never the case in practice, granted). This works because char is promoted to int for the shift, see this SO answer for an explanation.

What you see is a macro, that make the following job :

(In order)

  • Make a bit shift to y (value : 3)
  • That take the address of x and pick the character in position y (into the string x)
  • Make a binary operation between the selected char and 0x80
  • Make a bit shift to the previous result (value: result of binary operation between y and 0x7)
  • Make a bit shift ti the previous result (value: 7 - (result of binary operation between y and 0x7))

Well, this is help you? I don't think so!

Because this macro is clairly unproper, and kind of tricky.

Bit mask, Binary operation, Binary shift...

If you can explain more precisly what you want to understand in this, maybe i can be helpfull.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top