标准库函数fopen
在<stdio.h>
中声明为:
FILE *fopen(const char * restrict filename, const char * restrict mode);
这也是函数原型在C标准中的出现方式。
为什么参数restrict
合格?
答案 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关键字限定stream
和buf
,以告诉编译器在FILE
范围内对setbuf
结构的修改对buf
的内容没有影响,反之亦然。
fopen
的实现可能也是如此,其中程序员告诉编译器FILE
由fopen
操纵的结构不与文件名重叠,也不与模式重叠。但是,对filename
和mode
进行限定是一种错误的承诺,因为它暗示了标准中不存在的约束。
我的结论是函数声明原型中的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。