Is the C restrict qualifier transitive? - c

While there are many examples [1][2][3] that address how the restrict keyword works, I am not completely sure if the restrictified relation is transitive on the pointers that it can point to. For instance, the following code declares a structure that contains an integer and a pointer to an integer.
typedef struct container_s {
int x;
int *i;
} container_s;
int bar(container_s *c, int *i) {
int* tmp = c->i;
*tmp = 5;
*i = 4;
return *tmp;
}
int main(){
return 0;
}
Does the compiler need an extra load instruction for the last access of tmp (the returned value) because it cannot infer that *i and *tmp do not alias?
If so, would this new definition fix that load?
int bar(container_s *c, int* restrict i) { ... }
EDIT
This case int bar(container_s *c, int * restrict i) { ... } removes the extract load when I produce LLVM IR (clang -S -O3 -emit-llvm). However, I do not understand why the next two modifications do not remove that final load when:
I update the definition of the function (is the restrict transitively considered for c->i?) to:
int bar(container_s * restrict c, int *i) { ... }
I update the structure as below (Why cannot the compiler infer that there is no need for an extra load?):
typedef struct container_s {
int x;
int * restrict i;
} container_s;
int bar(container_s *c, int *i) { ... }

I'm having trouble seeing how transitivity applies here, but I can speak to your example.
Does the compiler need an extra load instruction for the last access of tmp (the returned value) because it cannot infer that *i and *tmp do not alias?
The compiler indeed cannot safely infer that *i and *tmp do not alias in your original code, as you have aptly demonstrated. It does not follow that the compiler specifically needs emit the load instruction implied by the abstract machine semantics of the * operator, but it does need to take care to deal with the aliasing issue somehow.
If so, would [restrict-qualifying parameter i] fix that load?
Adding restrict-qualification to parameter i in the function definition places the following additional requirement on the behavior of the program (derived from the text of C2011, 6.7.3.1/4): during each execution of bar(), because i is (trivially) based on i, and *i is used to access the object it designates, and that designated object is modified during the execution of bar() (via *i at least), every other lvalue used to access the object designated by *i shall also have its address based on i.
*tmp is accessed, and its address, tmp, is not based on i. Therefore, if i == tmp (that is, if on some call, i == c->i) then the program fails to conform. Its behavior is undefined in that case. The compiler is free to emit code that assumes the program conforms, so in particular, in the restrict-qualified case it can emit code that assumes both that the statement
*i = 4;
does not modify *tmp, and that the statement
*tmp = 5;
does not modify *i. Indeed, it seems consistent with the definition and express intent of restrict that compilers be free to make precisely those assumptions.
In particular, if the compiler chooses to handle the possibility of aliasing in the original code by performing the possibly-redundant load of *tmp, then in the restrict-qualified version it might choose to optimize by omitting that load. However, the resulting machine code is by no means required to differ in any way between the two cases. That is, you cannot, in general, rely on the compiler to make use of all the optimizations available to it.
Update:
The followup questions ask why clang does not perform a particular optimization under particular circumstances. Before anything else, it is essential to reiterate that C compilers have no responsibility whatever for performing any particular optimization that may be possible for given source code, except as they themselves document. Therefore, one generally cannot draw any conclusions from the fact that a given optimization is not performed, and it is rarely useful to ask why a given optimization was not performed.
About as far as you can go -- and I am interpreting the questions in this light -- is to ask whether the optimization in question is one that a conforming compiler could have performed. In this case the standard underscores that by taking the unusual step of clarifying that restrict imposes no optimization obligation on implementations:
A translator is free to ignore any or all aliasing implications of uses of restrict.
(C2011, 6.7.3.1/6)
With that said, on to the questions.
In this code variant, *tmp is an lvalue whose address is based on restrict-qualified pointer c. The object it designates is accessed via that lvalue within the scope of the function, and also modified within that scope (via *tmp, so the compiler can certainly see it). The address of *i is not based on c, so the compiler is free to assume that *i does not alias *tmp, just as in the original question.
This case is different. Although it is permitted to restrict-qualify struct members, restrict has effect only when it qualifies an ordinary identifier (C2011, 6.7.3.1/1), which struct member names are not (C2011, 6.2.3). In this case, restrict has no effect, and to ensure conforming behavior, the compiler must account for the possibility that c->i and *i (and *tmp) are aliases.

"would this new header fix that load?", --> No. The restrict refers to i, and accessing its fields:
... requires that all accesses to that object use, directly or indirectly, the value of that particular pointer... C11 §6.7.3 8
but does not extend to qualify those fields when they in turn are used to access other data.
#include<stdio.h>
typedef struct container_s {
int x;
int *i;
} container_s;
int bar(container_s * c, int* restrict i) {
int* tmp = c->i;
*tmp = 5;
*i = 4;
return *tmp;
}
int main(void) {
int i = 42;
container_s s = { 1, &i };
printf("%d\n", bar(&s, &i));
printf("%d\n", i);
printf("%d\n", *(s.i));
}
Output
4
4
4

Related

gcc/clang usage of restrict keyword for local variables and struct fields

Can't figure out how to convince gcc/clang that my pointers don't intersect; for what I see, it looks like restrict is only honored if specified in function arguments and ignored otherwise. Here is my code:
#if defined(_MSC_VER) || defined(__cplusplus)
#define restrict __restrict
#endif
struct s {
int sz;
int *a;
int *b;
};
struct s_r {
int sz;
int *restrict a;
int *restrict b;
};
void foo_dumb_struct(struct s *s, int c) {
int sz = s->sz;
for(int i = 0; i != sz; ++i) {
s->a[i] = s->b[0] + c;
}
}
void foo_restricted_arrays(int sz,
int *restrict a, int *restrict b,
int c) {
for(int i = 0; i != sz; ++i) {
a[i] = b[0] + c;
}
}
void foo_restricted_struct(struct s_r *s, int c) {
int sz = s->sz;
for(int i = 0; i != sz; ++i) {
s->a[i] = s->b[0] + c;
}
}
void foo_restricted_subcall(struct s *s, int c) {
foo_restricted_arrays(s->sz, s->a, s->b, c);
}
void foo_restricted_cast(struct s *s, int c) {
int sz = s->sz;
int *restrict a = s->a;
int *restrict b = s->b;
for(int i = 0; i != sz; ++i) {
a[i] = b[0] + c;
}
}
Icc is fine with this code, but gcc/clang generates re-read of b[0] on each iteration for foo_restricted_struct and foo_restricted_cast, for all architectures I could test with godbolt. Any time it is used in function arguments (including nested functions or C++ lambdas) it is fine and extra load is removed. https://cellperformance.beyond3d.com/articles/2006/05/demystifying-the-restrict-keyword.html suggests that it actually worked as I want it to, but I'm not sure their gcc wasn't customized for cell specifically.
Is my usage of restrict wrong, or gcc/clang only implements restrict for function arguments and nothing else?
for what I see, it looks like restrict is only honored if specified in function arguments
That characterization sounds like you think restrict qualification carries some kind of obligation to optimize more aggressively. That explicitly is not the case:
A translator is free to ignore any or all aliasing implications of uses of restrict.
(C standard, paragraph 6.7.3.1/6)
I do grant that it is a bit surprising that a compiler that takes advantage of restrict qualification to perform additional optimizations in some cases would not do the same in other, similar cases, but that does not imply that either the code or the compiler is in any way wrong. (But do bear in mind Eric's observation about restrict-qualified structure members). Also, however, the examples presented may not all be as similar to each other as you suppose.
Is my usage of restrict wrong, or gcc/clang only implements restrict for function arguments and nothing else?
Although the standard defines semantics for restrict-qualified block-scoped variables, they cannot really be used for much. Restrict qualification is a means to move some responsibility for dependency analysis from compiler to programmer, but the programmer has no more information to bring to bear than the compiler already has in a case such as the foo_restricted_cast() example. I'd say that yes, your usage there is (semantically) wrong, because you have no sound basis on which to make the implicit guarantee that local variables a and b will not alias each other. I rate GCC's and Clang's behavior prudent and appropriate in that light, and ICC's somewhat rash.
As for restrict-qualified structure members, I disagree with the other answer's assertion that no semantics are defined for them. It is true that the identifiers of structure members are not "ordinary identifiers", but the wording of the standard's definition of restrict semantics seems to be specifically crafted with a view toward covering structure members via the declarations of the ordinary identifiers of structure objects containing them. The language can certainly be read that way, and it is more than usually fraught if it is meant otherwise.
Thus, I think that the case of foo_restricted_struct() has well-defined semantics, and furthermore that icc is justified in taking advantage of the non-aliasing assertions conveyed by the restrict qualification of the argument structure's members, just as if they were direct function parameters. It is impossible for me to say why gcc and Clang do not also take advantage of the optimization options that proceed, but again, they have no obligation to do so.
On the other hand, foo_restricted_subcall() exhibits a semantic problem similar to the one in foo_restricted_cast(). I suppose that there is an outside chance that it is for that reason that GCC and/or Clang avoids more aggressively optimizing foo_restricted_struct(), which foo_restricted_subcall() calls with a semantically problematic argument. Probably, though, these compilers just don't perform a deep enough analysis to see the optimization opportunity in this case.
restrict is not defined by the C standard for members of structures.
The formal definition of restrict in 6.7.3.1 begins with “Let D be a declaration of an ordinary identifier…”
6.2.3 1 defines defines “ordinary identifiers“ to exclude members of structures or unions:
… Thus, there are separate name spaces for various categories of identifiers, as follows:
— label names (disambiguated by the syntax of the label declaration and use);
— the tags of structures, unions, and enumerations (disambiguated by following any of the
keywords struct, union, or enum);
— the members of structures or unions; each structure or union has a separate name space for its members (disambiguated by the type of the expression used to access the member via the . or
-> operator);
— all other identifiers, called ordinary identifiers (declared in ordinary declarators or as enumeration constants).
Footnote 126 in 6.7.2.1 explicitly tells us structure members are not ordinary identifiers:
A structure or union cannot contain a member with a variably modified type because member names are not ordinary identifiers as defined in 6.2.3.

Does access through pointer change strict aliasing semantics?

With these definitions:
struct My_Header { uintptr_t bits; }
struct Foo_Type { struct My_Header header; int x; }
struct Foo_Type *foo = ...;
struct Bar_Type { struct My_Header header; float x; }
struct Bar_Type *bar = ...;
Is it correct to say that this C code ("case one"):
foo->header.bits = 1020;
...is actually different semantically from this code ("case two"):
struct My_Header *alias = &foo->header;
alias->bits = 1020;
My understanding is that they should be different:
Case One considers the assignment unable to affect the header in a Bar_Type. It only is seen as being able to influence the header in other Foo_Type instances.
Case Two, by forcing access through a generic aliasing pointer, will cause the optimizer to realize all bets are off for any type which might contain a struct My_Header. It would be synchronized with access through any pointer type. (e.g. if you had a Foo_Type which was pointing to what was actually a Bar_Type, it could access through the header and reliably find out which it had--assuming that's something the header bits could tell you.)
This relies on the optimizer not getting "smart" and making case two back into case one.
The code bar->header.bits = 1020; is exactly identical to struct My_Header *alias = &bar->header; alias->bits = 1020;.
The strict aliasing rule is defined in terms of access to objects through lvalues:
6.5p7 An object shall have its stored value accessed
only by an lvalue expression that has one of the following
types:
The only things that matter are the type of the lvalue, and the effective type of the object designated by the lvalue. Not whether you stored some intermediate stages of the lvalue's derivation in a pointer variable.
NOTE: The question was edited since the following text was posted. The following text applies to the original question where the space was allocated by malloc, not the current question as of August 23.
Regarding whether the code is correct or not. Your code is equivalent to Q80 effective_type_9.c in N2013 rev 1571, which is a survey of existing C implementations with an eye to drafting improved strict aliasing rules.
Q80. After writing a structure to a malloc’d region, can its members be accessed via a pointer to a different structure type that has the same leaf member type at the same offset?
The stumbling block is whether the code (*bar).header.bits = 1020; sets the effective type of only the int bits; or of the entire *bar. And accordingly, whether reading (*foo).header.bits reads an int, or does it read the entire *foo?
Reading only an int would not be a strict aliasing violation (it's OK to read int as int); but reading a Bar_Struct as Foo_Struct would be a violation.
The authors of this paper consider the write to set the effective type for the entire *bar, although they don't give their justification for that, and I do not see any text in the C Standard to support that position.
It seems to me there's no definitive answer currently for whether or not your code is correct.
The fact that you have two structures which contain My_Header is a red herring and complicates your thinking without bringing anything new to the table. Your problem can be stated and clarified without any struct (other than My_Header ofcourse).
foo->header.bits = 1020;
The compiler clearly knows which object to modify.
struct My_Header *alias = &foo->header;
alias->bits = 1020;
Again the same is true here: with a very rudimentary analysis the compiler knows exactly which object the alias->bits = 1020; modifies.
The interesting part comes here:
void foo(struct My_Header* p)
{
p->bits = 1020;
}
In this function the pointer p can alias any object (or sub-object) of type My_header. It really doesn't matter if you have N structures who contain My_header members or if you have none. Any object of type My_Header could be potentially modified in this function.
E.g.
// global:
struct My_header* global_p;
void foo(struct My_Header* p)
{
p->bits = 1020;
global_p->bits = 15;
return p->bits;
// the compiler can't just return 1020 here because it doesn't know
// if `p` and `global_p` both alias the same object or not.
}
To convince you that the Foo_Type and Bar_Type are red herrings and don't matter look at this example for which the analysis is identical to the previous case who doesn't involve neither Foo_Type nor Bar_type:
// global:
struct My_header* gloabl_p;
void foo(struct Foo_Type* foo)
{
foo->header.bits = 1020;
global_p->bits = 15;
return foo->header.bits;
// the compiler can't just return 1020 here because it doesn't know
// if `foo.header` and `global_p` both alias the same object or not.
}
The way N1570 p5.6p7 is written, the behavior of code that accesses individual members of structures or unions will only be defined if the accesses are performed using lvalues of character types, or by calling library functions like memcpy. Even if a struct or union has a member of type T, the Standard (deliberately IMHO) refrains from giving blanket permission to access that part of the aggregate's storage using seemingly-unrelated lvalues of type T. Presently, gcc and clang seem to grant blanket permission for accessing structs, but not unions, using lvalues of member type, but N1570 p5.6p7 doesn't require that. It applies the same rules to both kinds of aggregates and their members. Because the Standard doesn't grant blanket permission to access structures using unrelated lvalues of member type, and granting such permission impairs useful optimizations, there's no guarantee gcc and clang will continue this behavior with with unrelated lvalues of member types.
Unfortunately, as can be demonstrated using unions, gcc and clang are very poor at recognizing relationships among lvalues of different types, even when one lvalue is quite visibly derived from the other. Given something like:
struct s1 {short x; short y[3]; long z; };
struct s2 {short x; char y[6]; };
union U { struct s1 v1; struct s2 v2; } unionArr[100];
int i;
Nothing in the Standard would distinguish between the "aliasing" behaviors of the following pairs of functions:
int test1(int i)
{
return unionArr[i].v1.x;
}
int test2a(int j)
{
unionArr[j].v2.x = 1;
}
int test2a(int i)
{
struct s1 *p = &unionArr[i].v1;
return p->x;
}
int test2b(int j)
{
struct s2 *p = &unionArr[j].v2;
p->x = 1;
}
Both of them use an lvalue of type int to access the storage associated with objects of type struct s1, struct s2, union U, and union U[100], even though int is not listed as an allowable type for accessing any of those.
While it may seem absurd that even the first form would invoke UB, that shouldn't be a problem if one recognizes support for access patterns beyond those explicitly listed in the Standard as a Quality of Implementation issue. According to the published rationale, the authors of the Standard thought compiler writers would to try to produce high-quality implementations, and it was thus not necessary to forbid "conforming" implementations from being of such low quality as to be useless. An implementation could be "conforming" without being able to handle test1a() or test2b() in cases where they would access member v2.x of a union U, but only in the sense that an implementation could be "conforming" while being incapable of correctly processing anything other than some particular contrived and useless program.
Unfortunately, although I think the authors of the Standard would likely have expected that quality implementations would be able to handle code like test2a()/test2b() as well as test1a()/test1b(), neither gcc nor clang supports them pattern reliably(*). The stated purpose of the aliasing rules is to avoid forcing compilers to allow for aliasing in cases where there's no evidence of it, and where the possibility of aliasing would be "dubious" [doubtful]. I've seen no evidence that they intended that quality compilers wouldn't recognize that code which takes the address of unionArr[i].v1 and uses it is likely to access the same storage as other code that uses unionArr[i] (which is, of course, visibly associated with unionArr[i].v2). The authors of gcc and clang, however, seem to think it's possible for something to be a quality implementation without having to consider such things.
(*) Given e.g.
int test(int i, int j)
{
if (test2a(i))
test2b(j);
return test2a(i);
}
neither gcc nor clang will recognize that if i==j, test2b(j) would access the same storage as test2a(i), even when though both would access the same element of the same array.

Does strict aliasing apply when using pointers to struct members?

Does test_func the following snippet trigger undefined behavior under the strict aliasing rules when the two arguments partially overlap?
That is the second argument is a member of the first:
#include <stdio.h>
typedef struct
{
//... Other fields
int x;
//... Other fields
} A;
int test_func(A *a, int *x)
{
a->x = 0;
*x = 1;
return a->x;
}
int main()
{
A a = {0};
printf("%d\n", test_func(&a, &a.x));
return 0;
}
Is the compiler allowed to think test_func will just return 0, based on the assumption that A* and int* will not alias? so the *x cannot overwrite the member?
Strict aliasing refers to when a pointer is converted to another pointer type, after which the contents are accessed. Strict aliasing means that the involved pointed-at types must be compatible. That does not apply here.
There is however the term pointer aliasing, meaning that two pointers can refer to the same memory. The compiler is not allowed to assume that this is the case here. If it wants to do optimizations like those you describe, it would perhaps have to add machine code that compares the pointers with each other, to determine if they are the same or not. Which in itself would make the function slightly slower.
To help the compiler optimize such code, you can declare the pointers as restrict, which tells the compiler that the programmer guarantees that the pointers are not pointing at the same memory.
Your function compiled with gcc -O3 results in this machine code:
0x00402D09 mov $0x1,%edx
Which basically means that the whole function was replaced (inlined) with "set a.x to 1".
But if I rewrite your function as
int test_func(A* restrict a, int* restrict x)
{
a->x = 0;
*x = 1;
return a->x;
}
and compile with gcc -O3, it does return 0. Because I have now told the compiler that a->X and x do not point at the same memory, so it can assume that *x = 1; does not affect the result and skip the line *x = 1; or sequence it before the line a->x = 0;.
The optimized machine code of the restrict version actually skips the whole function call, since it knows that the value is already 0 as per your initialization.
This is of course a bug, but the programmer is to blame for it, for careless use of restrict.
This is not a violation of strict aliasing. The strict aliasing rule says (simplified) that you can access the value of an object only using an lvalue expression of a compatible type. In this case, the object you're accessing is the member x of main's a variable. This member has type int. And the expression you use to access it (*x) also has type int. So there's no problem.
You may be confusing strict aliasing with restrict. If you had used the restrict keyword in the declaration of one of the pointer parameters, the code would be invalid because restrict prevents you from using different pointers to access the same object - but this is a different issue than strict aliasing.

Whether this specific example is undefined behavior for restrict keyword from C99?

I went through all answers but I can't get the answer about whether the following example is undefined behavior.
It's the same example as in 6.7.3.1 of C99 spec.
EXAMPLE 3 The function parameter declarations
void h(int n, int * restrict p, int * restrict q, int * restrict r)
{
int i;
for (i = 0; i < n; i++)
p[i] = q[i] + r[i];
}
illustrate how an unmodified object can be aliased through two restricted pointers. In particular, if a and b are disjoint arrays, a call of the form h(100, a, b, b) has defined behavior, because array b is not modified within function h.
In short, it is explicitly mentioned it's defined behavior if b is not modified within function h. However, whether is it undefined behavior if a call of the form h(100, a, a, b)?
A little bit more backgrounds why I want to make it clear. There are some basic functions which we want to use in in-place or out-of-place manner. In order to reduce the effort, it is desired if we don't need to provide both h(int n, int * restrict p, int * restrict q, int * restrict r) and h_inplace(int n, int * restrict p, int * restrict q). From current observation, it seems gcc, clang, icc, msvc can give correct result even if we call it as the form of h(100, a, a, b). However, we definitely don't want to have the risk if it is undefined behavior (which means it may be wrong from other compilers or future versions of gcc, clang, icc, msvc). What do you think?
h(100, a, a, b) obviously causes UB because p and q were promised not to alias each other, and the code writes through one of them. See C11 6.7.3.1/4:
If L is used to access the value of the object X that it designates, and X is also modified (by any means), then the following requirements apply: [...] Every other lvalue used to access the value of X shall also have its address based on P
Writing through p accesses the value of an object (called X here), and modifies it. Therefore every lvalue used to access the object within the function must be generated from p. However q is not generated from p.
It is a hint to the code optimizer. Using restrict ensures it that it can store a pointer variable in a CPU register and not have to flush an update of the pointer value to memory so that an alias is updated as well.
Whether or not it takes advantage of it depends heavily on implementation details of the optimizer and the CPU. Code optimizers already are heavily invested in detecting non-aliasing since it is such an important optimization.
However, if the restrict keyword is used and the function is declared as
void updatePtrs(size_t *restrict ptrA, size_t *restrict ptrB, size_t *restrict val);
then the compiler is allowed to assume that ptrA, ptrB, and val point to different locations and updating one pointer will not affect the other pointers. The programmer, not the compiler, is responsible for ensuring that the pointers do not point to identical locations.
If as a programmer you are not ensuring that ptrA , ptrB points to different location then it is obvious you are breaking the rule which may result into undefined behavior .
It is fairly clear, as the M.M's first answer states, that h(100, a, a, b) is undefined behavior, but it is possible to create a low-overhead version of h that does have defined behavior (IMO: I'm currently having an argument with Richard about this.)
void h2(int n, int * restrict p, int * restrict q_opt, int * restrict r)
{
int i;
int * restrict q = (p == q_opt) ? p : q_opt;
for (i = 0; i < n; i++)
p[i] = q[i] + r[i];
}
An even simpler version (but which requires the caller to know the magic value) has the new line:
int * restrict q = q_opt ? q_opt : p;
and the caller gets the in-place behavior by calling h2(100, a, 0, b).
In both cases the q pointer is either distinct from p according to the rules of restrict or derived from p (and not q), and so no conflict arises.
In particular, the discussion and rationale in the standard are actually pretty good, I think. The reason for the restriction is that faster code can sometimes be made by first copying any of the data addressed by a restrict pointer into another memory space (per argument) operating on it there, and then copying the results back. This is particularly true when the extra memory copy is into registers, and doubly true when the compiler is allowed to unroll loops, all of which are good go-fast tools.
The strategy here clearly works fine when a source at q_opt is copied to another location before being used (before use, outside the loop), whether it refers to p or to a different array. It would clearly fail if the user called h2(100, a+1, a, r), because that would still be a violation of the rules of restrict.

Is it possible to cast pointers from a structure type to another structure type extending the first in C?

If I have structure definitions, for example, like these:
struct Base {
int foo;
};
struct Derived {
int foo; // int foo is common for both definitions
char *bar;
};
Can I do something like this?
void foobar(void *ptr) {
((struct Base *)ptr)->foo = 1;
}
struct Derived s;
foobar(&s);
In other words, can I cast the void pointer to Base * to access its foo member when its type is actually Derived *?
You should do
struct Base {
int foo;
};
struct Derived {
struct Base base;
char *bar;
};
to avoid breaking strict aliasing; it is a common misconception that C allows arbitrary casts of pointer types: although it will work as expected in most implementations, it's non-standard.
This also avoids any alignment incompatibilities due to usage of pragma directives.
Many real-world C programs assume the construct you show is safe, and there is an interpretation of the C standard (specifically, of the "common initial sequence" rule, C99 §6.5.2.3 p5) under which it is conforming. Unfortunately, in the five years since I originally answered this question, all the compilers I can easily get at (viz. GCC and Clang) have converged on a different, narrower interpretation of the common initial sequence rule, under which the construct you show provokes undefined behavior. Concretely, experiment with this program:
#include <stdio.h>
#include <string.h>
typedef struct A { int x; int y; } A;
typedef struct B { int x; int y; float z; } B;
typedef struct C { A a; float z; } C;
int testAB(A *a, B *b)
{
b->x = 1;
a->x = 2;
return b->x;
}
int testAC(A *a, C *c)
{
c->a.x = 1;
a->x = 2;
return c->a.x;
}
int main(void)
{
B bee;
C cee;
int r;
memset(&bee, 0, sizeof bee);
memset(&cee, 0, sizeof cee);
r = testAB((A *)&bee, &bee);
printf("testAB: r=%d bee.x=%d\n", r, bee.x);
r = testAC(&cee.a, &cee);
printf("testAC: r=%d cee.x=%d\n", r, cee.a.x);
return 0;
}
When compiling with optimization enabled (and without -fno-strict-aliasing), both GCC and Clang will assume that the two pointer arguments to testAB cannot point to the same object, so I get output like
testAB: r=1 bee.x=2
testAC: r=2 cee.x=2
They do not make that assumption for testAC, but — having previously been under the impression that testAB was required to be compiled as if its two arguments could point to the same object — I am no longer confident enough in my own understanding of the standard to say whether or not that is guaranteed to keep working.
That will work in this particular case. The foo field in the first member of both structures and hit has the same type. However this is not true in the general case of fields within a struct (that are not the first member). Items like alignment and packing can make this break in subtle ways.
As you seem to be aiming at Object Oriented Programming in C I can suggest you to have a look at the following link:
http://www.planetpdf.com/codecuts/pdfs/ooc.pdf
It goes into detail about ways of handling oop principles in ANSI C.
In particular cases this could work, but in general - no, because of the structure alignment.
You could use different #pragmas to make (actually, attempt to) the alignment identical - and then, yes, that would work.
If you're using microsoft visual studio, you might find this article useful.
There is another little thing that might be helpful or related to what you are doing ..
#define SHARED_DATA int id;
typedef union base_t {
SHARED_DATA;
window_t win;
list_t list;
button_t button;
}
typedef struct window_t {
SHARED_DATA;
int something;
void* blah;
}
typedef struct window_t {
SHARED_DATA;
int size;
}
typedef struct button_t {
SHARED_DATA;
int clicked;
}
Now you can put the shared properties into SHARED_DATA and handle the different types via the "superclass" packed into the union.. You could use SHARED_DATA to store just a 'class identifier' or store a pointer.. Either way it turned out handy for generic handling of event types for me at some point. Hope i'm not going too much off-topic with this
I know this is an old question, but in my view there is more that can be said and some of the other answers are incorrect.
Firstly, this cast:
(struct Base *)ptr
... is allowed, but only if the alignment requirements are met. On many compilers your two structures will have the same alignment requirements, and it's easy to verify in any case. If you get past this hurdle, the next is that the result of the cast is mostly unspecified - that is, there's no requirement in the C standard that the pointer once cast still refers to the same object (only after casting it back to the original type will it necessarily do so).
However, in practice, compilers for common systems usually make the result of a pointer cast refer to the same object.
(Pointer casts are covered in section 6.3.2.3 of both the C99 standard and the more recent C11 standard. The rules are essentially the same in both, I believe).
Finally, you've got the so called "strict aliasing" rules to contend with (C99/C11 6.5 paragraph 7); basically, you are not allowed to access an object of one type via a pointer of another type (with certain exceptions, which don't apply in your example). See "What is the strict-aliasing rule?", or for a very in-depth discussion, read my blog post on the subject.
In conclusion, what you attempt in your code is not guaranteed to work. It might be guaranteed to always work with certain compilers (and with certain compiler options), and it might work by chance with many compilers, but it certainly invokes undefined behavior according to the C language standard.
What you could do instead is this:
*((int *)ptr) = 1;
... I.e. since you know that the first member of the structure is an int, you just cast directly to int, which bypasses the aliasing problem since both types of struct do in fact contain an int at this address. You are relying on knowing the struct layout that the compiler will use and you are still relying on the non-standard semantics of pointer casting, but in practice this is significantly less likely you give you problems.
The great/bad thing about C is that you can cast just about anything -- the problem is, it might not work. :) However, in your case, it will*, since you have two structs whose first members are both of the same type; see this program for an example. Now, if struct derived had a different type as its first element -- for example, char *bar -- then no, you'd get weird behavior.
* I should qualitfy that with "almost always", I suppose; there're a lot of different C compilers out there, so some may have different behavior. However, I know it'll work in GCC.

Resources