Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are fopen arguments restrict qualified in the C Standard and <stdio.h> header file?

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?

like image 841
chqrlie Avatar asked Feb 13 '16 23:02

chqrlie


People also ask

Where does Fopen find files?

In the current directory.

What does Fopen return?

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.


3 Answers

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.

like image 69
chqrlie Avatar answered Sep 28 '22 08:09

chqrlie


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.

like image 31
John Bollinger Avatar answered Sep 28 '22 09:09

John Bollinger


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.

like image 45
David Hoelzer Avatar answered Sep 28 '22 08:09

David Hoelzer