12

考虑以下:

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 编译器,但都没有生成反映上述优化的代码。这让我怀疑这种优化违反了规范。如果不允许,规范中的哪里是这样说的?

注意:我的问题受到这个 SO question的启发

4

4 回答 4

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不能和tmpfoo 中的自动变量有相同的地址。因此,foo不能将其参数传回给bar,并且不允许给定的优化。

于 2013-06-06T09:44:13.580 回答
1

简要阅读这个 SO question这个 wikipedia enrty表明,restrict 关键字只能在函数的参数中起作用。但是,阅读C99 标准,特别是第 6.7.3.1 节,可以清楚地表明,restrict适用于使用的整个上下文restrict。所以通过使用

void foo(int *restrict p);

您保证唯一读取和写入指向的内存块p将是 via p

然而,即使有了这些信息,在编译时foo,编译器也不知道bar将如何处理它发送的信息。例如,考虑:

void bar (unsigned long long int *p) {
    *p = ((unsigned long long int) p) % 2000;
    }

结果取决于指针集的值,这意味着在编译时foo您建议的优化假设无法确定,因为如果进行了您建议的优化,结果会有所不同。

于 2013-06-06T08:34:31.070 回答
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;
}
于 2013-06-05T07:45:57.313 回答
0

这两个代码是不等价的:在第一种情况下,函数“bar”接收“tmp”的指针(并且可能使用值),这是一个本地(并且未初始化!)变量。在第二种情况下,“bar”直接作用于“p”,通常它会在“*p”中找到不同的值。如果“bar”函数将其参数声明为“仅输出”(例如,在 M$ VS 中通过 OUT 宏),情况会有所不同,因为变量的初始值将(应该被)忽略。(注意:大多数 VS 版本实际上将 OUT 宏定义为什么都没有。太伤心了......)

于 2013-06-04T12:33:50.480 回答