I have this struct:
typedef struct {
int *array;
int first;
int last;
} arguments;
and I would be interested in having a different struct with some added arguments:
typedef struct {
int *array;
int first;
int last;
// only used in children
int read_fd;
int write_fd;
} child_arguments;
but I have some functions that expect arguments
void do_something(arguments* args) { ... }
Is it safe to do this kind of cast:
void do_something_in_child(child_arguments* child_args) {
do_something( (arguments*) child_args );
}
I imagine that since C doesn't reorder members in a struct, and elements added afterwards shouldn't affect the padding of old elements, this might be safe. But is it? And if it is, is it C compliant or is it technically undefined behavior but safe in most compilers?
In the C language as originally specified by Dennis Ritchie, the syntax structPtr->member
was defined as yielding an lvalue of the member's type which was displaced from the original pointer by the member's offset, all in a manner agnostic with regard to whether the pointer identified an object of the specific structure type in question. If mujltiple structures shared a Common Initial Sequence, members of the CIS would have the same types and offset, allowing structures containing those members to be used interchangeably.
In an effort to make the performance of C competitive with that of FORTRAN/Fortran when performing the kinds of tasks for which FORTRAN/Fortran was designed to be suitable, the authors of the Standard were pressured to allow a compiler given something like:
struct position { double x,y,z; };
struct velocity { double dx,dy,dz; };
void apply_motions( struct position *pos, struct velocity *vel, int n)
{
int i;
for (i=0; i<n; i++)
{
pos[i].x += vel[i].dx;
pos[i].y += vel[i].dy;
pos[i].z += vel[i].dz;
}
}
to use the fact that struct position
and struct velocity
are different types to justify ignoring the possibility that a value which is written via pos[i].x
in one iteration might be read via vel[i].dx
in another iteration. The authors of the Standard recognized the necessity of providing a means by which compilers could be told that code using certain structures would rely for correctness upon compilers upholding the Common Initial Sequence guarantees with respect to those structures. Rather than creating a new syntax, the authors of the Standard recognized that programs would seldom define unions containing structures that wouldn't be used in that fashion, and that unlike any new syntactic constructs that might be created for that purpose, definitions of such unions would have no adverse effect on existing compilers that would universally honor Common Initial Sequence guarantees by default. The Standard thus states:
One special guarantee is made in order to simplify the use of unions: 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. Two structures share a common initial sequence if corresponding members have compatible types (and, for bit-fields, the same widths) for a sequence of one or more initial members.
Unfortunately, the authors of clang and gcc refuse to recognize unions as serving that purpose, and demand that programmers either use compiler-specific constructs instead, or else use a compiler flag that completely disables all type-based aliasing optimizations. So far as I can tell, every compiler configuration that upholds Common Initial Sequence guarantees in the presence of a completed union type definition would be visible according to ordinary rules of scope will uphold such do even without such a definition, and those which wouldn't uphold the CIS guarantees in the absence of such a union won't do so even if one is defined. As a consequence, almost no code bothers defining such unions, and instead relies upon having compilers configured to uphold Common Initial Sequence guarantees.