是否`常量T * restrict`保证指向的对象,以不被修改? [英] Does `const T *restrict` guarantee the object pointed-to isn’t modified?
问题描述
考虑以下code:
无效doesnt_modify(const int的*);INT美孚(INT * N){
* N = 42;
doesnt_modify(N);
回报* N;
}
其中, doesnt_modify
的定义不是编译器可见。因此,它必须假定, doesnt_modify
改变对象的 N
点,必须读 * ñ
的收益
之前(最后一行不能由替换返回42;
)。
假设, doesnt_modify
不修改 * N
。我想到了以下允许优化:
INT foo_r(INT * N){
* N = 42;
{/ *新的范围是很重要的,我想。 * /
const int的*限制n_restr = N;
doesnt_modify(n_restr);
返回* n_restr;
}
}
这都有的 doesnt_modify
调用者告诉编译器 * N
没有被修改的缺点,而不是函数本身可以告诉通过其原型编译器。只要限制
在声明中参数-qualifying到 doesnt_modify
不足够,比照。 <一href=\"http://stackoverflow.com/questions/28819855/is-top-level-volatile-or-restrict-significant-in-a-function-prototype\">Is顶级挥发性
或限制
显著[...]?。
在与 GCC编译-std = C99 -O3 -S
(或锵使用相同的选项),所有的功能被编译成等价组装,全部重新阅读 42
从 * N
。
-
请问一个编译器被允许做这种优化(按
替换最后一行返回42;
)的foo_r
?如果没有,是否有一个(可移植的,如果可能的话)的方式来告诉编译器doesnt_modify
不修改它什么参数指向?有没有办法做的编译器理解和利用? -
是否有功能有UB(提供
doesnt_modify
不会修改其参数的指针对象)?
为什么我认为,限制
可以帮助这里(从C11(n1570)6.7.3.1的正式定义限制
,P4 [EMPH我的。):
[在这种情况下, B
是 foo_r
,的内部块P
是 n_restr
, T
是 const int的
和 X
是>按,我想。]
在
B的每次执行
,让→
是具有任何左值&安培; →
根据P
。如果→
用于访问对象X的值
,它指定,和X
也被修改(通过任何方式),然后应满足以下要求:T
不得常量-qualified 。 [...]
块引用>$铛--version
(基于LLVM 3.5.0)的Ubuntu版本铿锵3.5.0-4ubuntu2(标签/ RELEASE_350 /决赛)
目标:x86_64的-PC-Linux的GNUgcc版本4.9.2是,在x86 32位的目标。
解决方案1版似乎清楚地
的正式定义指定限制
(C11 6.7.3.1)。对于以下code:const int的*限制P = N;
doesnt_modify(P);
返回* P;6.7.3.1中使用的符号是:
- 乙 - code的该块
- P - 变量
P
- T -
* P
是const int的
- X - 的(非const)中等正指向
P
- → - 左值
* P
是我们感兴趣的是6.7.3.1/4(部分):
在
B的每次执行
,让→
是具有任何左值&安培; →
根据P
。如果→
用于访问对象X的值
,它指定和X
也被修改(通过任何方式),那么应满足以下要求:T
不得const限定
[...]
如果这些要求得不到满足,则该行为是未定义的。
块引用>注意
T
是const限定。因此,如果X
以任何方式该块(在该块调用函数中包括)期间修改的行为是不确定的。因此编译器可以优化仿佛
doesnt_modify
没有修改X
。2版是编译器有点难度。 6.7.6.3/15说,顶级预选赛没有原型兼容性考虑 - 虽然他们并不完全忽略
所以,虽然原型说:
无效doesnt_modify2(const int的*限制P);
它仍然可能是因为函数体被声明为
无效doesnt_modify2(const int的* P)
,因此可能会修改* P
。我的结论是,当且仅当编译器可以看到
doesnt_modify2
的定义和确认P
是声明限制
在定义的参数列表那么这将是能够执行优化。Consider the following code:
void doesnt_modify(const int *); int foo(int *n) { *n = 42; doesnt_modify(n); return *n; }
where the definition of
doesnt_modify
isn’t visible for the compiler. Thus, it must assume, thatdoesnt_modify
changes the objectn
points to and must read*n
before thereturn
(the last line cannot be replaced byreturn 42;
).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; } }
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. Simplyrestrict
-qualifying the parameter todoesnt_modify
in the declaration doesn’t suffice, cf. "Is top-levelvolatile
orrestrict
significant [...]?".When compiling with
gcc -std=c99 -O3 -S
(or Clang with the same options), all functions are compiled to equivalent assembly, all re-reading the42
from*n
.
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?Does any function have UB (provided
doesnt_modify
doesn’t modify its argument’s pointee)?Why I think,
restrict
could help here (From C11 (n1570) 6.7.3.1 "Formal definition ofrestrict
", p4 [emph. mine]):[In this case,
B
is the inner block offoo_r
,P
isn_restr
,T
isconst int
, andX
is the object denoted by*n
, I think.]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 version is 4.9.2, on an x86 32bit target.
解决方案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;
the symbols used in 6.7.3.1 are:
- 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 in6.7.3.1/4 (partial):
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.Note that
T
is const-qualified. Therefore, ifX
is modified in any way during this block (which includes during the call to a function in that block), the behaviour is undefined.Therefore the compiler can optimize as if
doesnt_modify
did not modifyX
.
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.
So although the prototype says:
void doesnt_modify2(const int *restrict 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
.My conclusion is that if and only if the compiler can see the definition for
doesnt_modify2
and confirm thatp
is declaredrestrict
in the definition's parameter list then it would be able to perform the optimization.这篇关于是否`常量T * restrict`保证指向的对象,以不被修改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!