First some references. The C99 Standard says this about restrict
in section 6.7.3:
An object that is accessed through a restrict-qualified pointer has a special association with that pointer. This association, defined in 6.7.3.1 below, requires that all accesses to that object use, directly or indirectly, the value of that particular pointer.117) The intended use of the
restrict
qualifier (like theregister
storage class) is to promote optimization, and deleting all instances of the qualifier from all preprocessing translation units composing a conforming program does not change its meaning (i.e., observable behavior).
And then (§6.7.3.1 "Formal definition of restrict
"):
Let
D
be a declaration of an ordinary identifier that provides a means of designating an objectP
as a restrict-qualified pointer to typeT
.If
D
appears inside a block and does not have storage classextern
, letB
denote the block. IfD
appears in the list of parameter declarations of a function definition, letB
denote the associated block. Otherwise, letB
denote the block of main (or the block of whatever function is called at program startup in a freestanding environment).In what follows, a pointer expression
E
is said to be based on objectP
if (at some sequence point in the execution ofB
prior to the evaluation ofE
) modifyingP
to point to a copy of the array object into which it formerly pointed would change the value ofE
.119) Note that ''based'' is defined only for expressions with pointer types.During each execution of
B
, letL
be any lvalue that has&L
based onP
. IfL
is used to access the value of the objectX
that it designates, andX
is also modified (by any means), then the following requirements apply:T
shall not be const-qualified. Every other lvalue used to access the value ofX
shall also have its address based onP
. Every access that modifiesX
shall be considered also to modifyP
, for the purposes of this subclause. IfP
is assigned the value of a pointer expressionE
that is based on another restricted pointer objectP2
, associated with blockB2
, then either the execution ofB2
shall begin before the execution ofB
, or the execution ofB2
shall end prior to the assignment. If these requirements are not met, then the behavior is undefined.
As some have pointed out, this illustrates the rules (Example 4 from the standard):
{
int * restrict p1;
int * restrict q1;
p1 = q1; // undefined behavior
{
int * restrict p2 = p1; // valid
int * restrict q2 = q1; // valid
p1 = q2; // undefined behavior
p2 = q2; // undefined behavior
}
}
Now, my first question is this: why is it okay to assign from an outer restricted pointer to an inner one?
My understanding is that nothing forbids this, which has clear aliasing:
int * restricted x = /* ... */ ;
{
int * restricted y = x;
*x = 3;
printf("%d\n", *y); // 3
*y = 4;
printf("%d\n", *x); // 4
}
Of course, the set of aliases is restricted to the two pointers.
Hence my second question: what is the difference assigning from outer to inner (allowed), but not from inner to outer (forbidden, e.g. p1 = q1;
in the first example above)?
restrict
in the parameter list to a function? – Lancinaterestrict
would have been more understandable if it described what the compiler was allowed to do than what the programmer is forbidden from doing, but I think the basic idea is thatrestrict
allows a compiler to behave as though any read through arestrict
pointer is performed anywhere in the execution sequence between the assignment of the pointer and the point the logical read occurs, and writes are allowed to behave as though they occur any time within the lifetime of the pointer beyond where the logical write occurs, save that reads and writes can't occur... – Ceraceous*p = 2; restrict q=p; doSomething(q); p=p2;
, a compiler would be allowed to defer the write to*p
until up to the point wherep
gets reassigned, which would make the write unsequenced with regard to anything done via q. – Ceraceousvoid foo(int *restrict a, int *restrict b)
andvoid bar(int *restrict c, int *restrict d)
, presumably you agree thatbar
should be able to callfoo(c, d)
. You're also allowed to "inline"foo
"by hand" by placing its implementation in a nested block. – Protegerestrict
is useful in a local variable likedouble* restrict foo = malloc(42 * sizeof(double));
– Purpleness