The standard library function fopen
is declared in <stdio.h>
as:
FILE *fopen(const char * restrict filename, const char * restrict mode);
This is also how the function prototype appears in the C Standard.
Why are the arguments restrict
qualified?
In the current directory.
The fopen() function returns a pointer to a FILE structure type that can be used to access the open file. Note: To use stream files (type = record) with record I/O functions, you must cast the FILE pointer to an RFILE pointer. A NULL pointer return value indicates an error.
There does not seem to be any compelling reason for fopen
arguments to be restrict
qualified in the prototype in <stdio.h>
.
restrict
qualifying a pointer is a promise by the programmer that the object pointed to by said pointer will only be accessed via this and any other pointer based on it within the given scope.
In a function prototype, such a promise is moot.
Nate Eldredge offered the explanation that it means you are not allowed to have filename and mode point to the same string. But this claim seems irrelevant and such a constraint is unneeded and not mentioned in the definition of fopen
in section 7.21.5.3 of the C Standard.
The prototype for setbuf
has the same restrict
qualifier for its arguments:
void setbuf(FILE * restrict stream, char * restrict buf);
I can understand why an implementer would qualify stream
and buf
with the restrict keyword to tell the compiler that a modification to the FILE
structure during the scope of setbuf
has no effect on the contents of buf
and vice versa.
The same might be true of an implementation of fopen
where the programmer tells the compiler that the FILE
structure manipulated by fopen
does not overlap the filename, nor the mode. But qualifying both filename
and mode
is a false promise since it implies a constraint that is not present in the Standard.
My conclusion is that restrict
qualifying arguments in function declaration prototypes is unneeded and deceptive. It reduces readability and induces false interpretations.
The formal definition of the meaning of restrict
is given in section 6.7.3.1 of C2011. Note in particular that elements of the parameter list of a function prototype that is not part of a function definition do not meet the conditions set forth in paragraph 1 of that section:
Let D be a declaration of an ordinary identifier that provides a means of designating an object P as a restrict-qualified pointer to type T.
The parameter list of such a prototype does not provide any means to make such a designation. Therefore, nothing in the section gives any direct effect to a restrict
qualifier in that context.
The best interpretation is probably as a notice to users of the function that the parameters are declared with the restrict
qualifier in the function definition, where that qualifier does have effect.
Note also, however, that restrict
is not a blanket promise of absence of aliasing. Instead it is a qualified promise that if the target of the restrict
-qualified pointer is modified in any way then it will be accessed only via that pointer, within the scope of the pointer.
Coming back around to fopen()
, then, the restrict
qualifiers in the function prototype have nothing to say about the definedness of the behavior of any call of the function. That is, this is not inherently undefined:
char s[] = "r";
FILE *f = fopen(s, s);
The execution of the function is another story -- or it would be if the pointer targets were not also const
-qualified. Supposing that the prototype expresses the effective qualifiers for the function's definition, if the two arguments alias each other and if their target were modified by the function, then the function would invoke undefined behavior if it accessed the target via the other pointer. Inasmuch as the const
qualification of the pointer targets means that the function would invoke UB by modifying the target in the first place, the restrict
qualification is moot.
The execution of the function is another story. Although the const
qualification of the parameter targets means that we should assume that fopen()
will not attempt to modify them via those pointers, the targets themselves are not necessarily const
. They could conceivably be modified outside fopen()
, via a non-const
lvalue, and that would still be relevant to restrict
qualification. Suppose that the prototype expresses the effective qualifiers for the function's definition. If the two arguments alias each other, and if their shared target is modified anywhere in the program, then fopen()
would invoke UB when it accessed the target via both pointers.
It amounts to a promise that the data will not be changed. There is no actual enforcement of this promise, as is often the case in C, but failing to live by this promise can result in undefined behavior.
Essentially, the restrict
qualifier there means that no other pointer will change the value of the filename or of the access mode while the file handle is valid. Here's an excerpt from a website that covers this that is quite good:
Restrict is a "no data hazards will be generated" contract between the programmer and the compiler. The compiler relies on this information to make optimizations. If the data is, in fact, aliased, the results are undefined and a programmer should not expect the compiler to output a warning. The compiler assumes the programmer is not lying.
So, why is it there? Because, when the fopen
library function was written, the writer decided to ask that you not change the strings that you pass it after you ask it to open a file. To be frank, I can't see why this would even be requested because, to my knowledge, once the file is open only the file descriptor matters and the file name and mode are never referred to again internally.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With