structure members and pointer alignment

2019-07-23 03:18发布

问题:

Is a pointer to the struct aligned as if it were a pointer to the first element?

or

Is a conversion between a pointer to a struct and a pointer to the type of its first member (or visa versa) ever UB?

(I hope they are the same question...)

struct element
{
    tdefa x;
    tdefb y;
}; 

int foo(struct element* e);
int bar(tdefa* a);

~~~~~

tdefa i = 0;
foo((struct element*)&i);

or

struct element e;
bar((tdefa*)&e);

Where tdefa and tdefb could be defined as any type

Background:

I asked this question and a user in a comment on one of the answers brought up C11 6.3.2.3 p7 that states:

"A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined"

However I am having trouble working out when this would become an issue, my understanding was that padding would allow all members of the struct to be aligned correctly. Have I misunderstood?

and if:

struct element e;
tdefa* a = &e.x;

would work then:

tdefa* a = (tdefa*)&e;

would too.

回答1:

There is never any initial padding; the first member of a struct is required to start at the same address as the struct itself.

You can always access the first member of a struct by casting a pointer to the whole struct, to be a pointer to the type of the first member.

Your foo example might run into trouble because foo will be expecting its argument to point to a struct element which in fact it does not, and there might be an alignment mismatch.

However the bar example and the final example is fine.



回答2:

A pointer to a structure always points to its initial member.

Here is the citation directly from C99 standard (6.7.2.1, paragraph 13), emphasis mine:

Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning

As for your foo and bar examples:

The call to bar will be fine, as bar expects a tdefa, which is exactly what it's getting.

The call to foo, however, is problematic. foo expects a full struct element, but you're only passing a tdefa (while the struct consists of both tdefa and tdefb).