为什么fopen参数限制在C标准和<stdio.h>头文件中限定?

时间:2016-02-13 23:09:54

标签: c language-lawyer libc

标准库函数fopen<stdio.h>中声明为:

FILE *fopen(const char * restrict filename, const char * restrict mode);

这也是函数原型在C标准中的出现方式。

为什么参数restrict合格?

3 个答案:

答案 0 :(得分:2)

fopen的原型中,restrict参数似乎没有任何令人信服的理由<stdio.h>合格。

restrict限定指针是程序员的一个承诺,即所述指针所指向的对象只能通过this以及在给定范围内基于它的任何其他指针来访问。

在函数原型中,这样的承诺没有实际意义。

Nate Eldredge提供的解释是,你不允许文件名和模式指向相同的字符串。但这种说法似乎无关紧要,这种约束是不必要的,在C标准第7.21.5.3节fopen的定义中没有提及。

setbuf的原型对其参数具有相同的restrict限定符:

void setbuf(FILE * restrict stream, char * restrict buf);

我可以理解为什么实现者会使用restrict关键字限定streambuf,以告诉编译器在FILE范围内对setbuf结构的修改对buf的内容没有影响,反之亦然。

fopen的实现可能也是如此,其中程序员告诉编译器FILEfopen操纵的结构不与文件名重叠,也不与模式重叠。但是,对filenamemode进行限定是一种错误的承诺,因为它暗示了标准中不存在的约束。

我的结论是函数声明原型中的restrict限定参数是不必要的和欺骗性的。它降低了可读性并导致错误的解释。

答案 1 :(得分:1)

这相当于承诺不会更改数据。没有实际执行这个承诺,就像C中的情况一样,但是没有遵守这个承诺会导致未定义的行为。

基本上,restrict限定符表示当文件句柄有效时,没有其他指针会更改文件名或访问模式的值。以下摘录自website,内容非常好:

  

限制是程序员和编译器之间的“不会产生数据危险”的合同。编译器依赖此信息进行优化。如果数据实际上是别名,则结果是未定义的,程序员不应指望编译器输出警告。编译器假定程序员没有说谎。

那么,为什么呢?因为,当写入fopen库函数时,作者决定要求您在要求它打开文件后不要更改传递它的字符串。坦率地说,我不明白为什么甚至会要求这样做,因为据我所知,一旦文件打开,只有文件描述符很重要,文件名和模式永远不会再在内部被引用。

答案 2 :(得分:1)

restrict的含义的正式定义见section 6.7.3.1 of C2011。请特别注意,函数原型的参数列表中不属于函数定义的元素不符合该部分第1段中规定的条件:

  

设D是普通标识符的声明,它提供了一种将对象P指定为类型T的限制限定指针的方法。

这种原型的参数列表没有提供任何方法来进行这样的指定。因此,该部分中的任何内容都不会对该上下文中的restrict限定符产生任何直接影响。

最佳解释可能是向函数用户发出通知,函数定义中使用restrict限定符声明参数,其中限定符 有效。

但是,请注意,restrict不是没有别名的一揽子承诺。相反,它是限定承诺,如果restrict - 限定指针的目标以任何方式被修改,那么它将仅在指针范围内通过该指针访问。 / p>

然后回到fopen(),函数原型中的restrict限定符对函数的任何调用的行为的定义无话可说。也就是说,这本身并不是未定义的:

char s[] = "r";
FILE *f = fopen(s, s);

该函数的执行是另一个故事 - 或者如果指针目标也不是const - 那就是合格的。假设原型表示函数定义的有效限定符,如果两个参数彼此别名,如果它们的目标被函数修改,那么如果函数通过函数访问目标,则函数将调用未定义的行为另一个指针。因为指针目标的const限定意味着函数会通过首先修改目标来调用UB,所以restrict限定是没有意义的。

该功能的执行是另一个故事。虽然参数目标的const限定意味着我们应该假设fopen()不会尝试通过这些指针修改它们,但目标本身不一定是const。可以想象,他们可以通过非fopen()左值来修改 const之外的,这仍然与restrict资格相关。假设原型表达函数定义的有效限定符。如果两个参数相互别名,并且如果它们的共享目标在程序中的任何位置被修改,那么fopen()将在通过两个指针访问目标时调用UB。