Why's that?
Correct me if I'm wrong, but I thought that the restrict keyword was
defined in terms of "accesses," which would includes reads as well as
writes?
The restrict keyword has its roots in the proposed noalias keyword,
which was proposed, disagreed over vehemently, and finally not
included in the original C standard ANSI89/ISO 90.
A weakness of C compared to other languages is the possibility of
aliasing. Let's take a trivial program like this:
#include <stdio.h>
int arr1 [5] = { 2, 4, 6, 8, 10 };
int arr2 [5] = { 2, 4, 6, 8, 10 };
void some_func(int *a, int *b, int count)
{
while (count--)
{
if (*a == *b)
{
*a = *b + 2;
}
++a;
}
}
int main(void)
{
int b = 2;
some_func(arr1, &b, 5);
some_func(arr2, arr2, 5);
printf("arr1 = { %d, %d, %d, %d, %d }\n",
arr1[0],arr1[1],arr1[2],arr1[3],arr1[4]);
printf("arr2 = { %d, %d, %d, %d, %d }\n",
arr2[0],arr2[1],arr2[2],arr2[3],arr2[4]);
return 0;
}
A simple optimization in some_func() would be for the compiler to read
*b once, and cache it in a local variable or register, instead of
reading it each time. But what if b is the address of one of the ints
in the range a[0] to a[count - 1]? Let's run it and look at the
output:
arr1 = { 4, 4, 6, 8, 10 }
arr2 = { 4, 6, 6, 8, 10 }
Both calls to some_func() were made with a count of 5 and an input
array of 5 ints containing 2, 4, 6, 8, and 10. And finally, both
calls were made with a pointer to an int having the value 2.
If the compiler performed the optimization of reading *b only once and
cacheing the value, both arrays would have had identical values after
being processed by some_func(), which would have been wrong. When
some_func() is called the second time, b points to arr2[0], so the
first pass of the loop causes *b to be modified. This causes *b to
match *a on the second pass of the loop, and the code correctly
modifies both arr2[0] and arr2[1].
The price of this flexibility in C is the fact that the compiler
cannot perform the optimization of caching the value of b, since
modifying an object through pointer a could modify what b points to.
If b was defined as a pointer to a const int, the compiler could
perform that optimization. On the other hand, if b was restrict
qualified, the compiler could perform that optimization. If it did,
the results of the second function call would be wrong, but that would
be the programmer's fault, for invoking undefined behavior.
The description of the restrict keyword in the standard is rather
complex, but it still boils down to informing the compiler that the
value of an object will not be modified unexpectedly via a different
lvalue. This allows, but does not require, the compiler to perform
certain optimizations when the programmer specifies, via the restrict
keyword, that an object will not be modified through another pointer.
One of the examples from the standard might be helpful in
understanding the intent:
====
10 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
= q + r;
}
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.
====
So even though the term "access" is used several times in the
definition of the restrict qualifier, it has no meaning for values
that are not modified at all within the scope of the qualifier.