Yeah, I was being a smartass.
Okay. You're gonna have to try harder next time; looks like
[almost] everyone missed it. ;-)
But the code is a perfectly valid
optimization [and obfuscation] for your average x86 platform
(although, as Noah points out, non-portable).
And since this is a kernel only ever meant to be run on an x86 this is
ok. Certainly obfuscated, but not that badly; I would prefer the union
version myself as it confused me at first.
I *strongly* recommend the union version over the current version.
Because at the moment, the code is just *begging* for some clever
maintainer to re-arrange the global variables so that the pointer
comes first, then the short, then the char[1] (in the interests of
removing some padding bytes, on compilers that do that). Once
someone does that, the code will become entirely broken and will
cause incredibly hard-to-find system bugs. In a kernel, that's
bad. :-(
I would bet it would work on
most current systems at any rate, you would only need to worry if one of
the last two where too small to hold 6 bytes.
Which is entirely possible. *Especially* on an x86, where
2-byte pointers are the easiest thing to use, in programs not
exceeding one segment's-worth of data. Again, crashes and
Bad Things. [Hmm... this optimization is looking less and less
"perfectly valid" the more I think about it...]
The portable
equivalent would have been
union {
char c_name[7];
struct {
char c_name[1];
unsigned short cl_len;
struct Capability *cl_list;
} rest;
} my_data;
In the case that no structure padding is used, and sizeof(short)==2
and sizeof(struct Capability *)==4, then we have a data layout that
basically replicates the x86-compiler-specific code.
Can we be sure that there is no padding in the x86 specific version?
No. But the code was obviously written with the *assumption* that
there wouldn't be any. (Oh, and BTW, it's not *really* x86-specific.
It's specific to any compiler+architecture with the right size
data, the right alignment requirements, and the right strategy for
allocating variables in memory.)
The portability issue is not a major issue in this case. This code is
meant to expose the underlying hardware through a secure interface; you
can't really do that portably.
But you *can* do *this* little bit of it portably. So why not
take the five minutes and make it portable? ...And if the modification
takes more than five minutes, then you're in more trouble than I
thought!
Also, this is a much used component
inside of a kernel. These objects will be passed with any system call
an application makes or tied to an internal representation of the
process (haven't gotten that far yet). It might be worth it in this
special case.
It's all just bits and bytes at the machine level. The compiler
really won't care whether you wrap things in a struct or not, trust
me.
*Some* optimizations do help dumb compilers in a real sense --
for example, loop unrolling or use of the 'register' keyword can
really help some compilers limp along. But in this case, I don't
see any way that wrapping up the variables safely could possibly
hurt your performance. They keep the same addresses and everything.
If you're really curious about performance questions, you
should compile both versions and examine the assembly output.
If you *still* want help, consult someone or some group where
your particular compiler or architecture is on-topic. I.e.,
not comp.lang.c, anymore, because this discussion IMHO has
drifted too far off-topic.
HTH,
-Arthur