`const T *restrict` 是否保证指向的对象不被修改? [英] Does `const T *restrict` guarantee the object pointed-to isn’t modified?
问题描述
考虑以下代码:
void doesnt_modify(const int *);
int foo(int *n) {
*n = 42;
doesnt_modify(n);
return *n;
}
doesnt_modify
的定义对编译器不可见.因此,它必须假设 doesnt_modify
更改了 n
指向的对象,并且必须在 return
之前读取 *n
(最后一行不能用return 42;
代替).
where the definition of doesnt_modify
isn’t visible for the compiler. Thus, it must assume, that doesnt_modify
changes the object n
points to and must read *n
before the return
(the last line cannot be replaced by return 42;
).
假设,doesnt_modify
不修改 *n
.我考虑了以下允许优化:
Assume, doesnt_modify
doesn’t modify *n
. I thought about the following to allow the optimization:
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
没有被修改,而不是函数本身可以通过它的原型.仅仅 restrict
-在声明中将参数限定为 doesnt_modify
是不够的,参见.是顶级volatile
或 restrict
重要 [...]?".
This has the drawback that the caller of doesnt_modify
has to tell the compiler *n
isn’t modified, rather than that the function itself could tell the compiler via its prototype. Simply restrict
-qualifying the parameter to doesnt_modify
in the declaration doesn’t suffice, cf. "Is top-level volatile
or restrict
significant [...]?".
当使用 gcc -std=c99 -O3 -S
(或具有相同选项的 Clang)编译时,所有函数都被编译为等效的程序集,所有函数都重新读取 42代码> 来自
*n
.
When compiling with gcc -std=c99 -O3 -S
(or Clang with the same options), all functions are compiled to equivalent assembly, all re-reading the 42
from *n
.
是否允许编译器为
foo_r
进行这种优化(用return 42;
替换最后一行)?如果没有,是否有(可移植的,如果可能的话)方法告诉编译器doesnt_modify
不会修改其参数指向的内容?编译器有没有办法理解和利用?
Would a compiler be allowed to do this optimization (replace the last line by
return 42;
) forfoo_r
? If not, is there a (portable, if possible) way to tell the compilerdoesnt_modify
doesn’t modify what its argument points to? Is there a way compilers do understand and make use of?
是否有任何函数具有 UB(假设 doesnt_modify
不修改其参数的指针)?
Does any function have UB (provided doesnt_modify
doesn’t modify its argument’s pointee)?
为什么我认为,restrict
在这里可以提供帮助(来自 C11 (n1570) 6.7.3.1 Formal definition of restrict
",p4 [emph. mine]):
Why I think, restrict
could help here (From C11 (n1570) 6.7.3.1 "Formal definition of restrict
", p4 [emph. mine]):
[在这种情况下,B
是foo_r
的内部块,P
是n_restr
,T
是const int
,X
是*n
表示的对象,我想.]
[In this case, B
is the inner block of foo_r
, P
is n_restr
, T
is const int
, and X
is the object denoted by *n
, I think.]
在B
的每次执行过程中,令L
为基于P
有&L
的任意左值.如果 L
用于访问它指定的对象 X
的值,并且 X
也被修改(以任何方式),则适用以下要求:T
不得为 const-qualified.[…]
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 位目标上.
Gcc version is 4.9.2, on an x86 32bit target.
推荐答案
restrict
(C11 6.7.3.1) 的正式定义似乎明确指定了版本 1.对于以下代码:
Version 1 seems clearly specified by the formal definition of restrict
(C11 6.7.3.1). For the following code:
const int *restrict P = n;
doesnt_modify(P);
return *P;
6.7.3.1 中使用的符号是:
the symbols used in 6.7.3.1 are:
- B - 代码块
- P - 变量
P
- T -
*P
的类型,即const int
- X -
P
指向的(非常量)int - L - 我们感兴趣的是左值
*P
- B - that block of code
- P - the variable
P
- T - the type of
*P
which isconst int
- X - the (non-const) int being pointed to by
P
- L - the lvalue
*P
is what we're interested in
6.7.3.1/4(部分):
6.7.3.1/4 (partial):
在B
的每次执行过程中,令L
为基于P
有&L
的任意左值.如果 L
用于访问它指定的对象 X
的值,并且 X
也被修改(通过任何方式),那么以下要求适用:T
不得为 const 限定[...]如果不满足这些要求,则行为未定义.
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 限定的.因此,如果 X
在此块期间以任何方式被修改(包括在调用该块中的函数期间),则行为未定义.
Note that T
is const-qualified. Therefore, if X
is modified in any way during this block (which includes during the call to a function in that block), the behaviour is undefined.
因此编译器可以像 doesnt_modify
没有修改 X
一样进行优化.
Therefore the compiler can optimize as if doesnt_modify
did not modify X
.
版本 2 对编译器来说有点困难.6.7.6.3/15 表示在原型兼容性中不考虑顶级限定符——尽管它们没有被完全忽略.
Version 2 is a bit more difficult for the compiler. 6.7.6.3/15 says that top-level qualifiers are not considered in prototype compatibility -- although they aren't ignored completely.
所以虽然原型说:
void doesnt_modify2(const int *restrict p);
它仍然可能是函数体被声明为 void doesnt_modify2(const int *p)
,因此可能会修改 *p
.
it could still be that the body of the function is declared as void doesnt_modify2(const int *p)
and therefore might modify *p
.
我的结论是,当且仅当编译器可以看到 doesnt_modify2
的定义并确认 p
在定义中声明为 restrict
参数列表就可以进行优化了.
My conclusion is that if and only if the compiler can see the definition for doesnt_modify2
and confirm that p
is declared restrict
in the definition's parameter list then it would be able to perform the optimization.
这篇关于`const T *restrict` 是否保证指向的对象不被修改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!