Simon said:
Hallvard said:
Roman said:
typedef struct cmd
{
unsigned int Cmd;
unsigned int Code;
unsigned int Data[1];
} CMD;
(...)
This is conformant:
#include <stddef.h>
#include <stdlib.h>
...
CMD *foo = malloc(sizeof(CMD) + sizeof(unsigned int) * whatever);
...
unsigned int *data_ptr =
(unsigned int *)((char *)foo + offsetof(CMD, Data[0]));
use data_ptr[larger than 0];
and this is not:
unsigned int *data_ptr = foo->Data;
use data_ptr[larger than 0];
The reason is that the implementation is allowed to "know" that the
expression foo->Data is just a 1-element array, so accessing elements
beyond that via the 'Data' member of CMD can fail. The offsetof hack
avoids that, since it's just an address computation based on foo.
Nice try, but I don't think you're right.
The compiler knows that foo is a pointer to CMD. It knows the size of
CMD.
Sorry, you are starting off wrong right there. Converting a pointer too
CMD* does not tell the compiler that the pointed-to object is a CMD. If
it did, we could hardly cast pointers back and forth at all.
Basically, C objects must be _accessed_ via an expression which has the
effective type of the object. Just pointing to the object with a
pointer to a different type is OK. There are exceptions, including:
Malloced memory have no unambiguous declared type, so in this case the
effective type of the object is the type of whatever was last stored
there. Access via character types cancels the rules, more or less.
(This is C99 section 6.5 - but the above hack is most relevant in C90,
since that doesn't support 'int Data[];'. Oh well
Anyway, the upshot is that ((char*)data_ptr + anything) above just a
pointer to some memory, the compiler does _not_ know that the contents
is inside a CMD. An array bounds checking implementation can know the
size of the malloced block at runtime - it can use pointers which
consist of (pointer to start of object, offset, object length). And it
can construct such a pointer for Data[] when accessed as the Data
member. But (char*)foo + offsetof(...) avoids that.
OTOH, you may end up with the right conlusion after all. If there is
padding behind Data[1] in the struct, and you assign a CMD to *foo, then
maybe the implementation is allowed to notice that data_ptr[1] is not a
valid object - and vice versa, if you assign to data_ptr[1] then *foo is
not valid. So maybe the "struct hack" strictly speaking is only valid
when Data[] has character type, since that cancels the rules. I don't
remember what the standard says about padding bytes, and I'm not up to
digging in standardese now.
There is an approach that is perhaps even more foolproof, and that is to
never associate the void pointer from malloc with the CMD type.
void *foo_void = malloc(sizeof(CMD) + sizeof(unsigned int) * whatever);
CMD *foo_CMD = foo_void;
Right idea, wrong problem
If my "conforming code" actually isn't,
then you can use a CMD* if you like, but not make the _malloced object_
start with a CMD. That is, use offsetof() to access all of CMD's
members, never use it directly. Also you couldn't assign it to a CMD.
Anyway, it gets ugly.
And I don't believe there could be any real alignment problems with
this, given that the compiler has guaranteed we can store at least one
unsigned int at the offset of Data.
Right.
On the other hand, perhaps there are alignment issues.
Nope. If you store the data where the compiler would have stored it,
and initially aligned well (like at the beginning of a malloced block),
then any resulting alignment problem must be because you are emulating a
compiler bug.
What if, when calculating the struct packing, the compiler packed the
members of CMD closer than is allowed for typical unsigned ints,
....then the struct used as an ordinary struct whose members are accesed
as ordinary struct members. So the compiler won't do that.
If I circumvent that system by applying the odd byte offset to a void
pointer from malloc, the compiler may not know that it needs to generate
code for an unaligned access, and thus it could fail at runtime.
If you deliberatly misalign your data, your code is buggy. The compiler
is not required to fix your bugs (as in, "generate code for an unaligned
access"). This is related to the hack with Data[] above.