`const T*restrict`是否保证指向的对象不被修改?

maf*_*fso 10 c const restrict-qualifier

请考虑以下代码:

void doesnt_modify(const int *);

int foo(int *n) {
    *n = 42;
    doesnt_modify(n);
    return *n;
}
Run Code Online (Sandbox Code Playgroud)

其中定义对doesnt_modify编译器不可见.因此,它必须假定,doesnt_modify将对象n指向更改并且必须*nreturn(最后一行不能被替换return 42;)之前读取.

假设,doesnt_modify不修改*n.我考虑了以下内容以允许优化:

int foo_r(int *n) {
    *n = 42;
    { /* New scope is important, I think. */
        const int *restrict n_restr = n;
        doesnt_modify(n_restr);
        return *n_restr;
    }
}
Run Code Online (Sandbox Code Playgroud)

这样做的缺点是调用者doesnt_modify必须告诉编译器*n没有被修改,而不是函数本身可以通过其原型告诉编译器.简单地restrict将参数限定doesnt_modify在声明中是不够的,参见 "是顶级volatile还是restrict重要的?".

当与编译gcc -std=c99 -O3 -S(或锵使用相同的选项),所有的功能被编译成等效组件,全部重新读取42*n.

  1. 是否允许编译器进行此优化(替换最后一行return 42;)foo_r?如果没有,是否有(可移植的,如果可能的话)方式告诉编译器doesnt_modify不修改其参数指向的内容?编译器有没有理解和利用的方式?

  2. 有没有函数有UB(假设doesnt_modify不修改其参数的指针)?

为什么我认为,restrict可以在这里提供帮助(来自C11(n1570)6.7.3.1" restrict"p4 [ emph.mine])的正式定义:

[在这种情况下,我认为B是内部块foo_r,Pn_restr,Tconst int,并且X是表示的对象*n.]

在每次执行期间B,L设为任何&L基于的左值P.如果L用于访问X它指定的对象的值,并且X也被修改(通过任何方式),则以下要求适用:T不应该是const限定的.[...]

$ clang --version
Ubuntu clang version 3.5.0-4ubuntu2 (tags/RELEASE_350/final) (based on LLVM 3.5.0)
Target: x86_64-pc-linux-gnu
Run Code Online (Sandbox Code Playgroud)

在x86 32位目标上,Gcc版本为4.9.2.

M.M*_*M.M 9

第1版似乎由restrict(C11 6.7.3.1)的正式定义明确规定.对于以下代码:

const int *restrict P = n;
doesnt_modify(P);
return *P;
Run Code Online (Sandbox Code Playgroud)

6.7.3.1中使用的符号是:

  • B - 那段代码
  • P - 变量P
  • T - 其类型*Pconst int
  • X - 指向的(非const)intP
  • L - 左值*P是我们感兴趣的

6.7.3.1/4(部分):

在每次执行期间B,L设为任何&L基于的左值P.如果L用于访问X它指定的对象的值,并且X也被修改(通过任何方式),则以下要求适用:T不应该是const限定的 [...]如果不满足这些要求,那么行为未定义.

请注意,这T是const限定的.因此,如果X在此块期间以任何方式修改(包括在调用该块中的函数期间),则行为未定义.

因此编译器可以优化,就像doesnt_modify没有修改一样X.


版本2对于编译器来说有点困难.6.7.6.3/15表示顶级限定符不在原型兼容性中考虑 - 尽管它们不会被完全忽略.

虽然原型说:

void doesnt_modify2(const int *restrict p);
Run Code Online (Sandbox Code Playgroud)

它可能仍然是函数的主体被声明为void doesnt_modify2(const int *p),因此可能会修改*p.

我的结论是,当且仅当编译器可以看到的定义doesnt_modify2和确认p声明restrict中定义的参数列表那么这将是能够执行优化.