Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Nermal

Moderator
Original poster
Staff member
Dec 7, 2002
20,664
4,086
New Zealand
I need to split a 32-bit integer (x) up into an array of individual bytes, and I currently have this code:

Code:
unsigned char xarray[4] =
{
    x & 0xFF,
    (x >> 8) & 0xFF,
    (x >> 16) & 0xFF,
    (x >> 24) & 0xFF
};

This works perfectly well. However, I need to convert multiple ints so I've ended up repeating the same sort of thing multiple times, which is a bit of a no-no.

As you can't easily return an array from a C function, I thought it would be best to create a macro. However, I've never made one before. Here's what I tried:

Code:
#define BYTE_ARRAY(x) (x & 0xFF, (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF)

void my_function(blah blah)
{
    unsigned char xarray[4] =
    {
        BYTE_ARRAY(x)
    };
}

This compiles and runs, but generates the wrong result. I thought the preprocessor did a direct substitution of the macro into the code, so I would have expected that this would return exactly the same compiled binary. What am I missing? Alternatively, is there a 'better' way to accomplish this without using a macro?
 

chown33

Moderator
Staff member
Aug 9, 2009
10,767
8,468
A sea of green
Can you post the input value 'x' to the macro, along with the expected value and actual value?

Just looking at the macro, I'm fairly sure the problem is the parens around the macro's replacement text. That is, the expansion is enclosed in parens, like this:
(x & 0xFF, (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF)
when I think it needs to be like this:
x & 0xFF, (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF

The reason I think it needs to be the latter instead of the former has to do with C's use of commas. A comma is both a delimiter between items in an array initializer, AND an operator that separates sub-expressions in a single expression. As a result, this:
(x & 0xFF, (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF)
evaluates as a single expression having the value of the last sub-expression, i.e. (x >> 24) & 0xFF. The net result of that as an array initializer is a single value, followed by 3 uninitialized array elements (or maybe 3 array elements initialized to 0; I'd have to test it).

In this expansion:
x & 0xFF, (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF
we have 4 separate expressions separated by the comma delimiter, as distinct from the comma operator. Since those are enclosed in { } following an array initialization assignment, then you should get 4 separate array values.

A word of warning: if the argument x is something that expands to a varying expression, such as a pointer with pre/post-increment, then the 'x' in each sub-expression will change. This is probably undesirable, so you should add a warning to your macro documentation about not using ++ or -- in arg expressions.

Another word of warning: I usually enclose each appearance of an arg in a macro expansion within parens. This ensures correct precedence order, if the 'x' happens to be an expression using shift operators or booleans like & and |. Safer example:
(x) & 0xFF, ((x) >> 8) & 0xFF, ((x) >> 16) & 0xFF, ((x) >> 24) & 0xFF
 
  • Like
Reactions: NoBoMac and Nermal

Nermal

Moderator
Original poster
Staff member
Dec 7, 2002
20,664
4,086
New Zealand
Just looking at the macro, I'm fairly sure the problem is the parens around the macro's replacement text.

Bingo! That's exactly what the problem was. Something I'd read said you needed them around the replacement text and I didn't try without. It's now doing exactly what it needs to do.

Thanks! :)
 

chown33

Moderator
Staff member
Aug 9, 2009
10,767
8,468
A sea of green
The parens around the replacement text are part of the replacement text. Sometimes that's the right thing to do, but it's not a universal rule. You must always be aware of how it will affect the expanded text.

C's normal precedence rules place function calls at a fairly high precedence. That is, they get evaluated early. The thing is, a macro looks like a function call to a human, but it isn't one to the C parser. By the time the C parser sees it, it's already been expanded.

The traditional example is getc(), which is a macro, but it looks exactly like fgetc(), which is a real function. Because an expanded macro is just a textual expansion, the replacement text of getc() SHOULD be enclosed in parens, so it maintains a precedence similar to a function-call. Without the parens, the apparently equivalent getc() and fgetc() are NOT equivalent, and weird things can happen if used in non-trivial expressions.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.