I have two source files I am linking:
src1.c:
#include <stdio.h>
typedef struct my_struct {
char a;
short b;
int c;
} my_struct;
my_struct x;
void add1();
int main()
{
x.a = 0;
x.b = 0;
x.c = 0;
add1();
printf("%x, %x, %x\n", x.a, x.b, x.c);
}
and src2.c:
#include <stdio.h>
typedef struct my_struct {
int c;
short b;
char a;
} my_struct;
extern my_struct x;
void add1(){
x.a += 1;
x.b += 1;
x.c += 1;
}
The output is "1, 0, 10001" due to the nature of the type definitions and alignment. However, this relies on the second byte of the struct to be 0x00 (which is a padding byte in the struct for src1.c).
Is this guaranteed behavior? Are padding bytes typically initialized to 0? Are there cases when they wouldn't be?
The two struct types in each source file are not compatible with each other because the members aren't declared in the same order.
This is spelled out in section 6.2.7p1 of the C standard:
Two types have compatible type if their types are the same. Additional
rules for determining whether two types are compatible are described
in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers, and in
6.7.6 for declarators. Moreover, two structure, union, or enumerated types declared in separate translation units are compatible
if their tags and members satisfy the following requirements: If one
is declared with a tag, the other shall be declared with the same tag.
If both are completed anywhere within their respective translation
units, then the following additional requirements apply: there shall
be a one-to-one correspondence between their members such that each
pair of corresponding members are declared with compatible types; if
one member of the pair is declared with an alignment specifier, the
other is declared with an equivalent alignment specifier; and if one
member of the pair is declared with a name, the other is declared with
the same name. For two structures, corresponding members shall be
declared in the same order. For two structures or unions,
corresponding bit-fields shall have the same widths. For two
enumerations, corresponding members shall have the same values
This means that my_struct x; and extern my_struct x; are not compatible, and declaring an identifier multiple times with different types triggerer undefined behavior, which loosely speaking means there are no guarantees at all what your program will do.
Unrelated to this, as far as padding bytes go, structures declared at file scope will have padding bytes initialized to 0 if the struct is not explicitly initialized.
The proper way to examine padding bytes in a struct is to use an unsigned char * to point to the start of the struct and iterate through the individual bytes,
Is this guaranteed behavior?
No. C 2018 6.5 7 specifies rules for when the behavior is defined if you try to access an object defined as one type using a different type. Accessing one type of structure, say A, with a different structure type, say B, has defined behavior only if:
B is compatible with A,
B is a qualified version of a type compatible with A, or
one of the members of B (possibly nested) is of type A or a qualified version of A.
Option 3 clearly does not apply. Regarding 1 and 2, the rules for compatibility of structures in different translation units are in C 2018 6.2.7 1, and they require there be a one-to-one correspondence between structure members in their names, types, and alignments. The structures do not have that, so they do not satisfy the requirements.
This means the behavior of accessing the my_struct x object defined in one translation unit using the my_struct type defined in the other translation unit is not defined by the C standard. In other words, the C standard does not guarantee the behavior.
In C implementations without any cross-unit optimization or other information transfer outside of the ordinary linkage, we can reason that using the my_struct in src2.c must access the bytes of the my_struct defined in src1.c because there is no other way to implement the behavior that the C standard does require.1 This would generally be bad programming practice, as, even if it is necessary to reinterpret the bytes of one type of my_struct as another type, there are ways to do that that are defined by the C standard.
Are padding bytes typically initialized to 0?
Yes, for objects with static or thread storage duration, C 2018 6.7.9 says “… if it is an aggregate, every member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;…”
Are there cases when they wouldn't be?
There are cases where initialization will not set the padding bits to zero. If a structure object is defined with automatic storage duration (the default inside a function definition) or is dynamically allocated, it will not be initialized at all. If you explicitly initialize a structure object, the C standard does not specify what the padding bytes will be set to; they will have unspecified values.
There are also cases where the padding bytes will not remain zero. Whenever you store a value in a structure or its members, the padding bytes may take on unspecified values, per C 2018 6.2.6.1 6. For example, in main, you have x.a = 0;. Since a is a char and is presumably followed by one byte of padding (due to the short that follows it), the compiler is allowed to implement this by clearing eight bits of a processor register and then issuing a 16-bit store to the structure. This will set the byte for the a member to zero and will set the padding byte to whatever happened to be in the other bits of the register.
Then reading that byte through the my_struct type in the other translation unit will get those other bits.
Footnote
1 This speaks to the particular code in the question. In other circumstances, there can be further complications. For example, suppose translation unit U defines an object X with type A and then translation unit V both attempts to access X (directly by name) with an incompatible type B and to access X through a pointer of type “pointer to A”. The compiler in V is entitled to assume the two access refer to different objects, so it has no responsibility to coordinate them. For example, if V writes to X through the pointer and then reads X by name, the compiler has no obligation to actually read the bytes of X from memory; it could use a previously read value it is holding in a register, since it has no reason to believe the write through the pointer changed X.
If you use the same compiler for both units, the padding rules should be the same, unless they change due to optimizations - try disabling them (-O0 switch for GNU C Compiler).
Related
The "struct inheritance technique" in C (as described in this question) is made possible by the fact that the C standard guarantees that the first member of a struct will never have any padding before it (?), and that the address of the first member will always be equal to the address of the struct itself.
This allows usage such as the following:
typedef struct {
// some fields
} A;
typedef struct {
A base;
// more fields
} B;
typedef struct {
B base;
// yet more fields
} C;
C* c = malloc(sizeof(C));
// ... init c or whatever ...
A* a = (A*) c;
// ... access stuff on a etc.
B* b = (B*) c;
// ... access stuff on b etc.
This question has two parts:
A. It seems to me this technique breaks the strict aliasing rule. Am I wrong, and if so, why?
B. Suppose that this technique is indeed legal. In that case, does it make a difference if A: we first store the object in an lvalue of its specific type, before down or up casting it to a different type, or B: if we cast it directly to the particular type desired at the moment, without first storing it in the lvalue of the specific type?
For example, are these three options all equally legal?
Option 1:
C* make_c(void) {
return malloc(sizeof(C));
}
int main(void) {
C* c = make_c(); // First store in a lvalue of the specific type
A* a = (A*) c;
// ... do stuff with a
C* c2 = (C*) a; // Cast back to C
// ... do stuff with c2
return 0;
}
Option 2:
C* make_c(void) {
return malloc(sizeof(C));
}
int main(void) {
A* a = (A*) make_c(); // Don't store in an lvalue of the specific type, cast right away
// ... do stuff with a
C* c2 = (C*) a; // Cast back to C
// ... do stuff with c2
return 0;
}
Option 3:
int main(void) {
A* a = (A*) malloc(sizeof(C)); // Don't store in an lvalue of the specific type, cast right away
// ... do stuff with a
C* c2 = (C*) a; // Cast to C - even though the object was never actually stored in a C* lvalue
// ... do stuff with c2
return 0;
}
A. It seems to me this technique breaks the strict aliasing rule. Am I wrong, and if so, why?
Yes, you are wrong. I'll consider two cases:
Case 1: The C is fully initialized
That would be this, for example:
C *c = malloc(sizeof(*c));
*c = (C){0}; // or equivalently, "*c = (C){{{0}}}" to satisfy overzealous compilers
In that case, all the bytes of the representation of a C are set, and the effective type of the object comprising those bytes is C. This comes from paragraph 6.5/6 of the standard:
If a value is stored into an object having no declared type through an
lvalue having a type that is not a character type, then the type of
the lvalue becomes the effective type of the object for that access
and for subsequent accesses that do not modify the stored value.
But structure and array types are aggregate types, which means that objects of such types contain other objects within them. In particular, each C contains a B identified as its member base. Because the allocated object is, at this point, effectively a C, it contains a sub-object that is effectively a B. One syntax for an lvalue referring to that B is c->base. The type of that expression is B, so it is consistent with the strict-aliasing rule to use it to access the B to which it refers. That has to be ok, else structures (and arrays) would not work at all, whether dynamically allocated or not.*
But, as discussed in my answer to your previous question, (B *)c is guaranteed to be equal (in value and type) to &c->base. Thus *(B *)c is another lvalue referring to the B that is the first member of *c. That the syntax of that expression is different from that of the previous lvalue we considered is of no account. It is an lvalue of type B, associated with an object of type B, so using it to access the object to which it refers is one of the cases allowed by the SAR.
None of this is any different from the statically and automatically allocated cases.
Case 2: The C is not fully initialized
That could be something like this:
C *c = malloc(sizeof(*c));
*(B *)c = (B){0};
We have thereby assigned to the initial B-sized portion of the allocated object via an lvalue of type B, so the effective type of that initial portion is B. The allocated space does not at this point contain an object of (effective) type C. We can access the B and its members, read or write, via any acceptably-typed lvalues referring to them, as discussed above. But we have a strict aliasing violation if we
attempt to read *c as a whole (e.g. C c2 = *c;);
attempt to read C members other than base (e.g. X x = c->another;); or
attempt to read the allocated object via an lvalue of most unrelated types (e.g. Unrelated_but_not_char u = *(Unrelated_but_not_char *) c;
The first two of those cases are of interest here, and they make sense in terms of the dynamically allocated object, when interpreted as a C, not being fully initialized. Similar incomplete-initialization cases can arise with automatically allocated objects, too; they also produce undefined behavior, but by different rules.
Note well, however, that there is no strict aliasing violation for any write to the allocated space, because any such write will (re)assign the effective type of (at least) the region that is written to.
And that brings us to the main tricksome bit. What if we do this:
C *c = malloc(sizeof(*c));
c->base = (B){0};
? Or this:
C *c = malloc(sizeof(*c));
c->another = 0;
The allocated object does not have any effective type before the first write to it (and in particular, it does not have effective type C), so do write-to-member expressions via *c even make sense? Are they well-defined? The letter of the standard might support an argument that they do not, but no implementation adopts such interpretation, and there is no reason to think that any ever would.
The interpretation most consistent with both the letter of the standard and universal practice is that writing through a member-access lvalue constitutes simultaneously writing to the member and to its host aggregate, thus setting the effective type of the whole region, even though only one member's value is written. Of course, that still does not make it ok to read members whose values have not been written -- because their values are indeterminate, not because of the SAR.
That leaves this case:
C *c = malloc(sizeof(*c));
*(B *)c = (B){0};
B b2 = c->base; // What about this?
That is, if the effective type of an initial region of the allocated space is B, can we use a member-access lvalue based on type C to read the stored value of that B region? Again, one might argue not, on the basis that there is no actual C, but in practice, no implementation makes that interpretation. The effective type of the object being read -- the initial region of the allocated space -- is the same as the type of the lvalue used for access, so in that sense there is no SAR violation. That the host C is wholly hypothetical is a question primarily of syntax, not semantics, because the same region can definitely be read as an object of the same type via an alternative expression.
* But the SAR nevertheless forestalls any debate on this point by providing that "an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union)" is among the types that may be accessed. This clears any ambiguity surrounding the position that accessing a member also constitutes accessing any objects containing it.
I believe this quote from C11 (ISO/IEC 9899:2011 §6.5 7) should answer some of your questions (my emphasis added):
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its
members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
Then more can be answered by this (ISO/IEC 9899:2011 §6.7.2.1 15):
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.
The rest can be answered by this snippet (ISO/IEC 9899:2011 §7.22.3 1):
The order and contiguity of storage allocated by successive calls to the
aligned_alloc, calloc, malloc, and realloc functions is unspecified. The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated).
In conclusion:
A. You're wrong. See the first and second quotes for the reasoning.
B. No, it doesn't make a difference. See the third quote (and maybe the first) for the reasoning.
Yes, the first element of the structure doesn't have any padding before it.
Second, when the type of an anonymous field is a typedef for a struct or union, code may refer to the field using the name of the typedef.
This is a good practice taken from the GCC manuals:
typedef struct {
// some fields
} A;
typedef struct {
A;
// more fields
} B;
typedef struct {
B;
// yet more fields
} C;
B get_B (struct C *c) { return c->B; } /* access B */
Please check Unnamed Structure and Union Fields
With an anonymous union declared in a struct, you can access the members directly. This made sense and I thought, like a normal union, you could only read from the most recent value that has been written to. Then I saw this
#include<stdio.h>
struct Scope
{
// Anonymous union
union
{
char alpha;
int num;
};
};
int main()
{
struct Scope x;
x.num = 65;
// Note that members of union are accessed directly
printf("x.alpha = %c, x.num = %d", x.alpha, x.num);
return 0;
}
What then, is the point if I can just access all of the variables all of the time? Why not just declare the variables in the scope of "Scope"?
According to C11 6.2.6.1, the value of the accessed member is unspecified. In general, it could be a trap representation, which triggers undefined behavior.
(If you changed char to unsigned char, it would be safe, since unsigned char cannot have trap representations. So your program would run to completion and print something, but the C standard does not specify what value would be printed for x.alpha.)
Of course, any given implementation may specify what value you actually get (e.g. the low byte, or the high byte, of x.num). So such code is most likely intended to work only on such implementations, and not meant to be portable or standard-conforming.
As Peter notes, all this is independent of whether you use anonymous unions or the old-fashioned kind.
The point is that struct Scope could have other members. A more realistic example:
struct Scope
{
union
{
int num;
uint8_t num_byte [sizeof(int)];
};
int foo;
};
Now you can access struct Scope members as obj.num or obj.num_byte[i]. After that union in memory, there will be a different variable foo, so clearly the union members can't get moved out to the struct.
If not for anonymous union, we'd have to type something like obj.name.num, where name is potentially just clutter.
Regarding reading different union members, your statement "you could only read from the most recent value that has been written to" is not true in C. Note that C and C++ are different here.
C17 6.5.2.3 states:
A postfix expression followed by the . operator and an identifier designates a member of
a structure or union object. The value is that of the named member,95) and is an lvalue if
the first expression is an lvalue.
where the foot note 95) is helpful:
95) If the member used to read the contents of a union object is not the same as the member last used to
store a value in the object, the appropriate part of the object representation of the value is reinterpreted
as an object representation in the new type as described in 6.2.6 (a process sometimes called ‘‘type
punning’’). This might be a trap representation.
The part in 6.2.6 referred to by the foot note:
When a value is stored in a member of an object of union type, the bytes of the object
representation that do not correspond to that member but do correspond to other members
take unspecified values.
Where an operator is applied to a value that has more than one object representation,
which object representation is used shall not affect the value of the result. Where a
value is stored in an object using a type that has more than one object representation for
that value, it is unspecified which representation is used, but a trap representation shall
not be generated.
What this means in plain English is that C allows type punning, but it's the programmers responsibility to ensure that it is feasible, with regards to alignment/padding, trap representations, endianess etc.
You can interpret same memory segment differently. Its widely used in protocols implementations. Where you receive buffer of bytes and based on various flags treat/decode it as needed.
In this example int and char are different size and byte order
I have two questions concerning bit fields when there are padding bits.
Say I have a struct defined as
struct T {
unsigned int x: 1;
unsigned int y: 1;
};
Struct T only has two bits actually used.
Question 1: are these two bits always the least significant bits of the underlying unsigned int? Or it is platform dependent?
Question 2: Are those unused 30 bits always initialized to 0? What does the C standard say about it?
Question 1: are these two bits always the least significant bits of the underlying unsigned int? Or it is platform dependent?
No, it is both system and compiler dependent. You can never assume or know that they are MSB or LSB.
Question 2: Are those unused 30 bits always initialized to 0? What do the C and C++ standards say about it?
Depends on how you initialize the struct. A struct at local scope which isn't initialized may contain garbage values in padding bits/bytes. A struct that is initialized with at least one initializer set, is guaranteed to contain zero even in padding bytes: my_struct = { something };.
Sources
The language-lawyer details of why the above works are somewhat complex.
C17 6.7.9/9 (emphasis mine) says this:
Except where explicitly stated otherwise, for the purposes of this subclause unnamed
members of objects of structure and union type do not participate in initialization.
Unnamed members of structure objects have indeterminate value even after initialization.
This means that we cannot trust padding bits/bytes at all.
But then there's this exception to the above rule (§20 emphasis mine):
If there are fewer initializers in a brace-enclosed list than there are elements or members
of an aggregate, or fewer characters in a string literal used to initialize an array of known
size than there are elements in the array, the remainder of the aggregate shall be
initialized implicitly the same as objects that have static storage duration.
Meaning that if there's at least one initializer, then the following rule of static storage initialization applies:
C17 6.7.9/10 (emphasis mine):
If an object that has static or thread storage duration is not initialized
explicitly, then: /--/
if it is an aggregate, every member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;
The C99 standard states that:
When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object
Consider the following code:
struct test {
int x[5];
char something;
short y[5];
};
...
struct test s = { ... };
char *p = (char *) s.x;
char *q = (char *) s.y;
printf("%td\n", q - p);
This obviously breaks the above rule, since the p and q pointers are pointing to different "array objects", and, according to the rule, the q - p difference is undefined.
But in practice, why should such a thing ever result in undefined behaviour? After all, the struct members are laid out sequentially (just as array elements are), with any potential padding between the members. True, the amount of padding will vary across implementations and that would affect the outcome of the calculations, but why should that result be "undefined"?
My question is, can we suppose that the standard is just "ignorant" of this issue, or is there a good reason for not broadening this rule? Couldn't the above rule be rephrased to "both shall point to elements of the same array object or members of the same struct"?
My only suspicion are segmented memory architectures where the members might end up in different segments. Is that the case?
I also suspect that this is the reason why GCC defines its own __builtin_offsetof, in order to have a "standards compliant" definition of the offsetof macro.
EDIT:
As already pointed out, arithmetic on void pointers is not allowed by the standard. It is a GNU extension that fires a warning only when GCC is passed -std=c99 -pedantic. I'm replacing the void * pointers with char * pointers.
Subtraction and relational operators (on type char*) between addresses of member of the same struct are well defined.
Any object can be treated as an array of unsigned char.
Quoting N1570 6.2.6.1 paragraph 4:
Values stored in non-bit-field objects of any other object type
consist of n × CHAR_BIT bits, where n is the size of an object of that
type, in bytes. The value may be copied into an object of type
unsigned char [ n ] (e.g., by memcpy); the resulting set of bytes is
called the object representation of the value.
...
My only suspicion are segmented memory architectures where the members
might end up in different segments. Is that the case?
No. For a system with a segmented memory architecture, normally the compiler will impose a restriction that each object must fit into a single segment. Or it can permit objects that occupy multiple segments, but it still has to ensure that pointer arithmetic and comparisons work correctly.
Pointer arithmetic requires that the two pointers being added or subtracted to be part of the same object because it doesn't make sense otherwise.
The quoted section of standard specifically refers to two unrelated objects such as int a[b]; and int b[5]. The pointer arithmetic requires to know the type of the object that the pointers pointing to (I am sure you are aware of this already).
i.e.
int a[5];
int *p = &a[1]+1;
Here p is calculated by knowing the that the &a[1] refers to an int object and hence incremented to 4 bytes (assuming sizeof(int) is 4).
Coming to the struct example, I don't think it can possibly be defined in a way to make pointer arithmetic between struct members legal.
Let's take the example,
struct test {
int x[5];
char something;
short y[5];
};
Pointer arithmatic is not allowed with void pointers by C standard (Compiling with gcc -Wall -pedantic test.c would catch that). I think you are using gcc which assumes void* is similar to char* and allows it.
So,
printf("%zu\n", q - p);
is equivalent to
printf("%zu", (char*)q - (char*)p);
as pointer arithmetic is well defined if the pointers point to within the same object and are character pointers (char* or unsigned char*).
Using correct types, it would be:
struct test s = { ... };
int *p = s.x;
short *q = s.y;
printf("%td\n", q - p);
Now, how can q-p be performed? based on sizeof(int) or sizeof(short) ? How can the size of char something; that's in the middle of these two arrays be calculated?
That should explain it's not possible to perform pointer arithmetic on objects of different types.
Even if all members are of same type (thus no type issue as stated above), then it's better to use the standard macro offsetof (from <stddef.h>) to get the difference between struct members which has the similar effect as pointer arithmetic between members:
printf("%zu\n", offsetof(struct test, y) - offsetof(struct test, x));
So I see no necessity to define pointer arithmetic between struct members by the C standard.
Yes, you are allowed to perform pointer arithmetric on structure bytes:
N1570 - 6.3.2.3 Pointers p7:
... When a pointer to an object is converted to a pointer to a character type,
the result points to the lowest addressed byte of the object. Successive increments of the
result, up to the size of the object, yield pointers to the remaining bytes of the object.
This means that for the programmer, bytes of the stucture shall be seen as a continuous area, regardless how it may have been implemented in the hardware.
Not with void* pointers though, that is non-standard compiler extension. As mentioned on paragraph from the standard, it applies only to character type pointers.
Edit:
As mafso pointed out in comments, above is only true as long as type of substraction result ptrdiff_t, has enough range for the result. Since range of size_t can be larger than ptrdiff_t, and if structure is big enough, it's possible that addresses are too far apart.
Because of this it's preferable to use offsetof macro on structure members and calculate result from those.
I believe the answer to this question is simpler than it appears, the OP asks:
but why should that result be "undefined"?
Well, let's see that the definition of undefined behavior is in the draft C99 standard section 3.4.3:
behavior, upon use of a nonportable or erroneous program construct or
of erroneous data, for which this International Standard imposes no
requirements
it is simply behavior for which the standard does not impose a requirement, which perfectly fits this situation, the results are going to vary depending on the architecture and attempting to specify the results would have probably been difficult if not impossible in a portable manner. This leaves the question, why would they choose undefined behavior as opposed to let's say implementation of unspecified behavior?
Most likely it was made undefined behavior to limit the number of ways an invalid pointer could be created, this is consistent with the fact that we are provided with offsetof to remove the one potential need for pointer subtraction of unrelated objects.
Although the standard does not really define the term invalid pointer, we get a good description in Rationale for International Standard—Programming Languages—C which in section 6.3.2.3 Pointers says (emphasis mine):
Implicit in the Standard is the notion of invalid pointers. In
discussing pointers, the Standard typically refers to “a pointer to an
object” or “a pointer to a function” or “a null pointer.” A special
case in address arithmetic allows for a pointer to just past the end
of an array. Any other pointer is invalid.
The C99 rationale further adds:
Regardless how an invalid pointer is created, any use of it yields
undefined behavior. Even assignment, comparison with a null pointer
constant, or comparison with itself, might on some systems result in
an exception.
This strongly suggests to us that a pointer to padding would be an invalid pointer, although it is difficult to prove that padding is not an object, the definition of object says:
region of data storage in the execution environment, the contents of
which can represent values
and notes:
When referenced, an object may be interpreted as having a particular
type; see 6.3.2.1.
I don't see how we can reason about the type or the value of padding between elements of a struct and therefore they are not objects or at least is strongly indicates padding is not meant to be considered an object.
I should point out the following:
from the C99 standard, section 6.7.2.1:
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.
It isn't so much that the result of pointer subtraction between members is undefined so much as it is unreliable (i.e. not guaranteed to be the same between different instances of the same struct type when the same arithmetic is applied).
I can't seem to wrap my head around certain parts of the C standard, so I'm coming here to clear up that foggy, anxious uncertainty that comes when I have to think about what such tricks are defined behaviour and what are undefined or violate the standard. I don't care whether or not it will WORK, I care if the C standard considers it legal, defined behaviour.
Such as this, which I am fairly certain is UB:
struct One
{
int Hurr;
char Durr[2];
float Nrrr;
} One;
struct Two
{
int Hurr;
char Durr[2];
float Nrrr;
double Wibble;
} Two;
One = *(struct One*)&Two;
This is not all I am talking about. Such as casting the pointer to One to int*, and dereferencing it, etc. I want to get a good understanding of what such things are defined so I can sleep at night. Cite places in the standard if you can, but be sure to specify whether it's C89 or C99. C11 is too new to be trusted with such questions IMHO.
I think that technically that example is UB, too. But it will almost certainly work, and neither gcc nor clang complain about it with -pedantic.
To start with, the following is well-defined in C99 (§6.5.2.3/6): [1]
union OneTwo {
struct One one;
struct Two two;
};
OneTwo tmp = {.two = {3, {'a', 'b'}, 3.14f, 3.14159} };
One one = tmp.one;
The fact that accessing the "punned" struct One through union must work implies that the layout of the prefix of struct Two is identical to struct One. This cannot be contingent on the existence of a union because the a given composite type can only have one storage layout, and its layout cannot be contingent on its use in a union because the union does not need to be visible to every translation unit in which the struct is used.
Furthermore, in C all types are no more than a sequence of bytes (unlike, for example, C++) (§6.2.6.1/4) [2]. Consequently, the following is also guaranteed to work:
struct One one;
struct Two two = ...;
unsigned char tmp[sizeof one];
memcpy(tmp, two, sizeof one);
memcpy(one, tmp, sizeof one);
Given the above and the convertibility of any pointer type to a void*, I think it is reasonable to conclude that the temporary storage above is unnecessary, and it could have been written directly as:
struct One one;
struct Two two = ...;
unsigned char tmp[sizeof one];
memcpy(one, two, sizeof one);
From there to the direct assignment through an aliased pointer as in the OP is not a very big leap, but there is an additional problem for the aliased pointer: it is theoretically possible for the pointer conversion to create an invalid pointer, because it's possible that the bit format of a struct Two* differs from a struct One*. Although it is legal to cast one pointer type to another pointer type with looser alignment (§6.3.2.3/7) [3] and then convert it back again, it is not guaranteed that the converted pointer is actually usable, unless the conversion is to a character type. In particular, it is possible that the alignment of struct Two is different from (more strict than) the alignment of struct One, and that the bit format of the more strongly-aligned pointer is not directly usable as a pointer to the less strongly-aligned struct. However, it is hard to see an argument against the almost equivalent:
one = *(struct One*)(void*)&two;
although this may not be explicitly guaranteed by the standard.
In comments, various people have raised the spectre of aliasing optimizations. The above discussion does not touch on aliasing at all because I believe that it is irrelevant to a simple assignment. The assignment must be sequenced after any preceding expressions and before any succeeding ones; it clearly modifies one and almost as clearly references two. An optimization which made a preceding legal mutation of two invisible to the assignment, would be highly suspect.
But aliasing optimizations are, in general, possible. Consequently, even though all of the above pointer casts should be acceptable in the context of a single assignment expression, it would certainly not be legal behaviour to retain the converted pointer of type struct One* which actually points into an object of type struct Two and expect it to be usable either to mutate a member of its target or to access a member of its target which has otherwise been mutated. The only context in which you could get away with using a pointer to struct One as though it were a pointer to the prefix of struct Two is when the two objects are overlaid in a union.
--- Standard references:
[1] "if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them anywhere that a declaration of the completed type of the union is visible."
[2] "Values stored in non-bit-field objects of any other object type consist of n × CHAR_BIT
bits, where n is the size of an object of that type, in bytes. The value may be copied into
an object of type unsigned char [n] (e.g., by memcpy)…"
[3] "A pointer to an object type may be converted to a pointer to a different object type… When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object."
C99 6.7.2.1 says:
Para 5
As discussed in 6.2.5, a structure is a type consisting of a sequence
of members, whose storage is allocated in an ordered sequence
Para 12
Each non-bit-field member of a structure or union object is aligned in
an implementation-defined manner appropriate to its type.
Para 13
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
That last paragraph covers your second question (casting the pointer to One to int*, and dereferencing it).
The first point - whether it is valid to "Downcast" a Two* to a One* - I could not find specifically addressed. It boils down to whether the other rules ensure that the memory layout of the fields of One and the initial fields of Two are identical in all cases.
The members have to be packed in ordered sequence, no padding is allowed at the beginning, and they have to be aligned according to type, but the standard does not actually say that the layout needs to be the same (even though in most compilers I am sure it is).
There is, however, a better way to define these structures so that you can guarantee it:
struct One
{
int Hurr;
char Durr[2];
float Nrrr;
} One;
struct Two
{
struct One one;
double Wibble;
} Two;
You might think you can now safely cast a Two* to a One* - Para 13 says so. However strict aliasing might bite you somewhere unpleasant. But with the example above you don't need to anyway:
One = Two.one;
A1. Undefined behaviour, because of Wibble.
A2. Defined.
S9.2 in N3337.
Two standard-layout struct (Clause 9) types are layout-compatible if
they have the same number of non-static data members and corresponding
non-static data members (in declaration order) have layout-compatible
types
Your structs would be layout compatible and thus interchangeable but for Wibble. There is a good reason too: Wibble might cause different padding in struct Two.
A pointer to a standard-layout struct object, suitably converted using
a reinterpret_cast, points to its initial member (or if that member is
a bit-field, then to the unit in which it resides) and vice versa.
I think that guarantees that you can dereference the initial int.