5

I have inherited some heavily obfuscated and poorly written PIC code to modify. There are two macros here:

#define TopByteInt(v) (*(((unsigned char *)(&v)+1)))
#define BottomByteInt(v) (*((unsigned char *)(&v)))

Is anyone able to explain what on earth they do and what that means please?

Thanks :)

Jayesh Bhoi
  • 24,694
  • 15
  • 58
  • 73
DiBosco
  • 838
  • 8
  • 21
  • They are macros to fetch the top and bottom byte half of a 16 bit integer type – Morten Jensen Aug 26 '14 at 11:16
  • 4
    If you can't decode these simple expressions then you are going to have trouble with more complicated code... which bit of it is unclear? – M.M Aug 26 '14 at 11:16
  • 1
    The "poorly written" part is mostly the useless parenthesis while a required (for some arguments) pair is missing: `#define TopByteInt(v) (*((unsigned char *)&(v) + 1))` would be better... – mafso Aug 26 '14 at 11:29
  • @MattMcNabb, I've been writing embedded code for over twenty years and have no difficulty in writing clear, easy-to-follow, well structured code. Which this is not. – DiBosco Aug 26 '14 at 12:03
  • @malso, I wasn't necessarily saying these macros were poorly written, just I could not fathom what they meant. Although, having had it explained to me, I think it's a bizarre way of writing it. – DiBosco Aug 26 '14 at 12:05
  • Many thanks to all for the explanations. – DiBosco Aug 26 '14 at 12:07
  • You've never seen aliasing a value of one type as another type? – M.M Aug 26 '14 at 12:14

3 Answers3

5

They access a 16-bit integer variable one byte at a time, allowing access to the most significant and least significant byte halves. Little-endian byte order is assumed.

Usage would be like this:

uint16_t v = 0xcafe;
const uint8_t v_high = TopByteInt(&v);
const uint8_t v_low  = BottomByteInt(&v);

The above would result in v_high being 0xca and v_low being 0xfe.

It's rather scary code, it would be cleaner to just do this arithmetically:

#define TopByteInt(v)    (((v) >> 8) & 0xff)
#define BottomByteInt(v) ((v) & 0xff)
unwind
  • 391,730
  • 64
  • 469
  • 606
1
(*((unsigned char *)(&v)))

It casts the v (a 16 bit integer) into a char (8 bits), doing this you get only the bottom byte.

(*(((unsigned char *)(&v)+1)))

This is the same but it gets the address of v and sum 1 byte, so it gets only the top byte.

It'll only work as expected if v is a 16 bits integer.

dfranca
  • 5,156
  • 2
  • 32
  • 60
  • 1
    Huh? The first macro clearly uses `&v` too, it doesn't "cast the integer". – unwind Aug 26 '14 at 11:19
  • Yes, you're right, it gets the first 8 bits portion stored in the address of v, that would be more precise. – dfranca Aug 26 '14 at 11:56
  • 1
    I like this explanation the best as it points out that the "+1" is accessing the next byte. That's a salient point I was not seeing. Thank you. – DiBosco Aug 26 '14 at 12:09
1

Ugg.

Assuming you are on a little-endian platform, that looks like it might meaningfully be recorded as

#define TopByteInt(v) (((v) >> 8) & 0xff)
#define BottomByteInt(v) ((v) & 0xff)

It is basically taking the variable v, and extracting the least significant byte (BottomByteInt) and the next more significant byte (TopByteInt) from that. 'TopByte' is a bit of a misnomer if v isn't a 16 bit value.

Tom Tanner
  • 9,244
  • 3
  • 33
  • 61