size &= -4; /* ensure word aligned */
Yes, of course, I forgot about size_t for some reason! size_t
is guaranteed unsigned, isn't it?
The size_t type is an unsigned integral type. It could, at least
technically speaking, be an alias for "unsigned short" or "unsigned
char", though, and those could widen (under the normal promotion
rules) to plain (signed) int, in which case "size &= -4" would
not do the right thing.
Practically speaking, "size &= -4" will work fine. But I actually
prefer to write "size &= ~3", or in this case, "size &= ~(size_t)3",
myself. This may be due to writing too much assembly code over
the years, and reading "&=~" as the "bit-clear operator".
In the oddball case -- where size_t is, e.g., unsigned short and
unsigned short widens to signed int -- my preferred method will
"do the right thing" even on a ones'-complement machine. But the
"&= -4" will work for all size_t variables on "sensible" C systems,
and even on "non-sensible but two's complement" systems. It also
has the arguable advantage of encoding the power of two more
obviously:
size &= -32; /* round down to multiple of 32 */
vs:
size &= ~(size_t)31; /* round down to multiple of 32 */
Then again, in my experience, one tends to want these things to
round up, rather than down, in which case:
size = (size + 31) & ~(size_t)31;
is appropriate (except when size > SIZE_T_MAX - 31).