请考虑以下事项:
extern void bar(int *restrict);
void foo(int *restrict p) {
int tmp;
bar(&tmp);
*p = tmp;
}
C99规范是否允许将foo优化为以下内容?
extern void bar(int *restrict);
void foo(int *restrict p) {
bar(p);
}
我在-O3模式下尝试了gcc,Clang和Intel Compiler,并且都没有生成反映上述优化的代码。这让我怀疑这种优化打破了规范。如果不允许,那么它在规范中的含义是什么?
注意:我的问题的灵感来自this SO question
答案 0 :(得分:18)
答案是肯定的否定,这是不允许的。
考虑如果foo和bar相互递归会发生什么。例如,bar
的实现:
void bar(int *restrict p)
{
static int q;
if (p == &q) {
printf("pointers match!\n");
} else if (p == NULL) {
foo(&q);
}
}
bar
永远不会取消引用p,因此限制限定符无关紧要。很明显,静态变量q
不能与foo中的自动变量tmp
具有相同的地址。因此,foo
无法将其参数传递回bar
,并且不允许给定的优化。
答案 1 :(得分:1)
要使编译器进行优化,必须确保无论bar
如何实现,以及如何调用foo
,明确定义的行为都不会改变。
由于编译器不知道bar
的实现和对foo
的调用,所以当它编译foo
时,这种情况的理论存在就足以阻止优化,即使它在现实中不会发生。
以下是这种情况的一个例子。重点是:
1.参数p
指向只写存储器(例如存储器映射的I / O)
2. bar
与只写指针一起使用是不安全的(可能是它写入然后再读回来,期望相同的值)。
函数foo
可以安全地与只写指针一起使用,因为它只写p
。即使bar
不安全也是如此,因为bar
永远不会获得p
。根据建议的优化,bar
会获得p
,这可能会造成麻烦。
以下是包含bar
的文件和foo
调用的示例。
static int increment;
void bar(int *restrict p) {
(*p)=0;
if (increment) (*p)++;
}
void foo(int *restrict p);
int main(int ac, char **av) {
int *p = get_io_addr(); /* Get a write-only memory mapped I/O address */
increment = atoi(av[1]);
foo(p);
return 0;
}
答案 2 :(得分:1)
简要介绍this SO question和this wikipedia enrty表明restrict关键字只能在函数的参数中生效。但是,特别是阅读the C99 standard,第6.7.3.1节,可以清楚地表明restrict
适用于使用restrict
的整个上下文。所以通过使用
void foo(int *restrict p);
您要保证p
指向的内存块的唯一读取和写入将通过p
。
但是,即使有了这些信息,在编译foo
时,编译器也不知道bar
将对它发送的信息做什么。例如,考虑:
void bar (unsigned long long int *p) {
*p = ((unsigned long long int) p) % 2000;
}
结果取决于指针集的值,这意味着在编译foo
时,您建议的优化假设无法确定,因为如果您建议进行优化,结果会有所不同。< / p>
答案 3 :(得分:0)
这两个代码不等价:在第一种情况下,函数“bar”接收“tmp”的指针(可能使用该值),即本地(和非初始化!)变量。在第二种情况下,“bar”直接在“p”上工作,通常它会在“* p”中找到不同的值。如果“bar”函数将其参数声明为“仅输出”(例如,通过OUT宏在M $ VS中),则情况会有所不同,因为变量的初始值将被(应该被忽略)。 (注意:大多数VS版本实际上都将OUT宏定义为什么都没有。太可悲了......)