考虑以下功能:
int bar(const int* __restrict x, void g())
{
int result = *x;
g();
result += *x;
return result;
}
我们需要从x
读两次因为调用g()
吗?或者是__restrict
ion足以保证g()
的调用无法访问/不会改变地址x
的值?
在this link,我们看到最受欢迎的编译器必须说明这一点(GodBolt;语言标准C99,平台AMD64):
clang是否正确地优化了第二次读取,或者不是吗?我在这里要求C和C ++,因为行为是相同的(感谢@PSkocik)。
相关信息和一些注意事项:
__restrict
(或__restrict__
)的读者可能想看看:What does the restrict keyword mean in C++?x
被标记为const
的事实在这里并不重要 - 如果我们放弃const
并且问题保持不变,我们会得到相同的行为。我认为这实际上是一个C问题,因为C实际上是具有restrict
的语言,附带了正式的规范。
管理restrict
使用的C标准的一部分是6.7.3.1:
1设D是普通标识符的声明,它提供了一种将对象P指定为类型T的限制限定指针的方法。
2如果D出现在块内并且没有存储类extern,则让B表示该块。如果D出现在函数定义的参数声明列表中,则让B表示关联的块。否则,让B表示main的块(或者在独立环境中在程序启动时调用的任何函数的块)。
3在下文中,指针表达式E被称为基于对象P if(在评估E之前执行B的某个序列点)修改P以指向其先前的数组对象的副本指向会改变E.137的值。注意''based''仅针对具有指针类型的表达式定义。
4在每次执行B期间,让L为任何具有基于P的L的左值。如果L用于访问它指定的对象X的值,并且X也被修改(通过任何方式),则以下要求apply:T不具有const限定条件。用于访问X值的每个其他左值也应具有基于P的地址。出于本子条款的目的,每次修改X的访问也应被视为修改P.如果为P分配了指针表达式E的值,该指针表达式E基于与块B2相关联的另一个受限指针对象P2,则B2的执行应在执行B之前开始,或者B2的执行应在该执行之前结束。分配。如果不满足这些要求,则行为未定义。
5这里B的执行意味着程序执行的一部分对应于具有标量类型的对象的生命周期和与B关联的自动存储持续时间。
我读它的方式,g()
的执行属于bar
块的执行,所以g()
不允许修改*x
和clang
是正确的优化第二次加载(IOW,如果*x
指的是非const全球,g()
不得修改那个全局)。