考虑以下代码:
void doesnt_modify(const int *);
int foo(int *n) {
*n = 42;
doesnt_modify(n);
return *n;
}
定义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;
}
}
这有一个缺点,即 doesnt_modify
的调用者必须告诉编译器 *n
没有被修改,而不是函数本身可以通过它的原型(prototype)告诉编译器。简单 restrict
- 将参数限定为 doesnt_modify
在声明中是不够的,cf。 “Is top-level volatile
or restrict
significant [...]?” .
使用 gcc -std=c99 -O3 -S
编译时(或具有相同选项的 Clang),所有函数都编译为等效的程序集,全部重新读取 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
的内部 block , P
是n_restr
, T
是const int
, 和 X
是由 *n
表示的对象,我想。]
During each execution of
B
, letL
be any lvalue that has&L
based onP
. IfL
is used to access the value of the objectX
that it designates, andX
is also modified (by any means), then the following requirements apply:T
shall not be const-qualified. […]
$ 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
Gcc 版本是 4.9.2,在 x86 32 位目标上。
最佳答案
版本 1 似乎由 restrict
(C11 6.7.3.1) 的正式定义明确指定。对于以下代码:
const int *restrict P = n;
doesnt_modify(P);
return *P;
6.7.3.1中使用的符号是:
- B - 那个代码块
- P - 变量
P
- T -
*P
的类型,即const int
- X -
P
指向的(非常量)int
- L - 左值
*P
是我们感兴趣的
6.7.3.1/4(部分):
During each execution of
B
, letL
be any lvalue that has&L
based onP
. IfL
is used to access the value of the objectX
that it designates, andX
is also modified (by any means), then the following requirements apply:T
shall not be const-qualified [...] If these requirements are not met, then the behavior is undefined.
请注意 T
是 const 限定的。因此,如果在此 block 期间(包括调用该 block 中的函数期间)以任何方式修改了 X
,则行为未定义。
因此编译器可以像 doesnt_modify
没有修改 X
一样进行优化。
版本 2 对于编译器来说有点困难。 6.7.6.3/15 表示在原型(prototype)兼容性中不考虑顶级限定符——尽管它们没有被完全忽略。
所以尽管原型(prototype)说:
void doesnt_modify2(const int *restrict p);
仍然可能是函数体声明为 void dont_modify2(const int *p)
,因此可能会修改 *p
。
我的结论是,当且仅当编译器可以看到 doesnt_modify2
的定义并确认 p
在定义中被声明为 restrict
参数列表,然后它就可以执行优化。
关于c - `const T *restrict` 能保证指向的对象没有被修改吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26346301/