4 回答
restrict
版本 1 似乎由(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 - 指向的(非常量)int
P
- L - 左值
*P
是我们感兴趣的
6.7.3.1/4(部分):
在 的每次执行期间
B
,让成为基于 的L
任何左值。如果用于访问它指定的对象的值,并且也被修改(通过任何方式),则适用以下要求:不应是 const-qualified [...] 如果不满足这些要求,则行为未定义。&L
P
L
X
X
T
请注意,这T
是 const 限定的。因此,如果X
在此块期间以任何方式修改(包括在调用该块中的函数期间),则行为未定义。
因此编译器可以像doesnt_modify
没有修改一样进行优化X
。
版本 2 对编译器来说有点困难。6.7.6.3/15 表示在原型兼容性中不考虑顶级限定符——尽管它们没有被完全忽略。
所以虽然原型说:
void doesnt_modify2(const int *restrict p);
它仍然可能是函数的主体被声明为void doesnt_modify2(const int *p)
,因此可能会修改*p
.
我的结论是,当且仅当编译器可以看到定义doesnt_modify2
并确认在定义的参数列表p
中声明restrict
的定义时,它才能执行优化。
通常,restrict
意味着指针没有别名(即只有它或从它派生的指针可用于访问指向的对象)。
使用const
,这意味着指向的对象不能被格式良好的代码修改。
然而,没有什么可以阻止程序员使用显式类型转换来打破规则以消除这种情况const
。然后编译器(已被程序员殴打提交)将允许尝试修改指向的对象而没有任何抱怨。严格来说,这会导致未定义的行为,因此允许任何可以想象的结果,包括 - 可能 - 修改指向的对象。
如果没有,是否有(如果可能,可移植)方法告诉编译器 dont_modify 不会修改其参数指向的内容?
没有这种办法。
当涉及指针和引用函数参数时,编译器优化器难以优化。因为该函数的实现可以抛弃 constness 编译器假设它T const*
与T*
.
因此,在您的示例中,调用后doesnt_modify(n)
它必须*n
从内存中重新加载。
请参阅2013 年主题演讲:Chandler Carruth:优化 C++ 的紧急结构。它也适用于 C。
在此处添加restrict
关键字不会更改上述内容。
restrict
在指针类型参数上同时使用限定符和const
在其目标类型上使用限定符会导致编译器假定在指针对象的生命周期内没有通过其中包含的指针或从它派生的任何指针访问的存储区域, 将在该指针的生命周期内通过任何方式进行修改。它通常对未使用相关指针访问的存储区域没有任何说明。
const restrict
对整个对象有影响的唯一情况是使用带有static
边界的数组语法声明指针的情况。在这种情况下,行为只会在可以读取整个数组对象的情况下定义(不调用 UB)。由于读取在函数执行期间更改的数组对象的任何部分都会调用 UB,因此代码将被允许假设数组的任何部分都不能以任何方式更改。
不幸的是,虽然知道函数实际定义的编译器以:
void foo(int const thing[restrict static 1]);
将有权假设*thing
在函数执行期间不会更改任何部分,即使对象可能是函数可以通过非派生的指针访问的对象thing
,函数原型包含此类限定符的事实不会强制其定义同样做。