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指向更改并且必须*n在return(最后一行不能被替换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.
是否允许编译器进行此优化(替换最后一行return 42;)foo_r?如果没有,是否有(可移植的,如果可能的话)方式告诉编译器doesnt_modify不修改其参数指向的内容?编译器有没有理解和利用的方式?
有没有函数有UB(假设doesnt_modify不修改其参数的指针)?
为什么我认为,restrict可以在这里提供帮助(来自C11(n1570)6.7.3.1" restrict"p4 [ emph.mine])的正式定义:
[在这种情况下,我认为B是内部块foo_r,P是n_restr,T是const 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.
第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中使用的符号是:
P*P是const intP*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中定义的参数列表那么这将是能够执行优化.