【问题标题】:Does `const T *restrict` guarantee the object pointed-to isn’t modified?`const T *restrict` 是否保证指向的对象不被修改?
【发布时间】:2014-12-08 09:22:23
【问题描述】:

考虑以下代码:

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; 替换)。

假设,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 没有被修改,而不是函数本身可以通过其原型告诉编译器。仅在声明中将restrict 限定为doesnt_modify 的参数是不够的,参见。 “Is top-level volatile or restrict significant [...]?”.

当使用gcc -std=c99 -O3 -S(或具有相同选项的Clang)编译时,所有函数都被编译为等效的程序集,所有函数都从*n重新读取42

  1. 是否允许编译器对foo_r 进行这种优化(用return 42; 替换最后一行)?如果没有,是否有(如果可能,可移植)方法告诉编译器doesnt_modify 不会修改其参数指向的内容?编译器有没有办法理解和利用?

  2. 是否有任何函数具有 UB(前提是 doesnt_modify 不修改其参数的指针)?

为什么我认为,restrict 可以在这里提供帮助(来自 C11 (n1570) 6.7.3.1 “restrict 的正式定义”,p4 [emph. mine]):

[这里Bfoo_r的内部块,Pn_restrTconst intX*n表示的对象,我想想。]

在每次执行B 期间,让L 成为基于P 具有&L 的任何左值。如果L 用于访问它指定的对象X 的值,并且X 也被修改(以任何方式),则适用以下要求:@ 987654362@ 不应是 const 限定的。 […]

$ 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 位目标上。

【问题讨论】:

  • doesnt_modify2 丢弃 const 性并修改指向的对象是合法的,只要对象本身没有声明为 const
  • @T.C.是的,这就是重点。问题是,如果restrict 在这里有什么不同。
  • 有趣——你保证没有别的东西给它起别名,而且这个特定的指针不能修改它。我很好奇这意味着什么。

标签: c constants restrict-qualifier


【解决方案1】:

版本 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(部分):

在每次执行B 期间,让L 成为基于P 具有&L 的任何左值。如果L 用于访问它指定的对象X 的值,并且X 也被修改(通过任何方式),则适用以下要求:T 不得为 const-合格 [...] 如果不满足这些要求,则行为未定义。

请注意,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 时,它才能执行优化。

【讨论】:

  • 我可能错了,但我认为const 可以合法地被抛弃。结果指针仍将“基于”原始指针,因此不受restrict 的约束。唯一会引发 UB 的情况是原始(指向)对象被声明为 const
  • 罢工吧。我错过了 6.7.3.1 中“T 不应被 const 限定”的意义。
  • Minor: 6.7.6.3/13 是关于存储类说明符(函数参数中唯一允许的一个是register),而不是关于限定符,因此不适用(尽管我同意重点是,顶级限定符通常在这里被忽略,我不清楚标准在哪里规定了这一点,但它至少是常见的解释)。
  • 已经为原型案例专门开了一个separate question,虽然现阶段看不到其他解释
  • @MattMcNabb:谢谢,我目前正在考虑从我的问题中编辑该部分并改为链接到您的问题。正如我目前所看到的,内联包装器可以:inline void doesnt_modify(const int *restrict n) { real_doesnt_modify(n); } 只是为了向编译器显示一些定义。你认为,我应该把这个包含在我的问题中吗? (你认为内联定义就足够了吗?)
【解决方案2】:

一般来说,restrict 表示指针没有别名(即只有它或从它派生的指针可以用来访问指向的对象)。

对于const,这意味着指向的对象不能被格式良好的代码修改。

然而,没有什么可以阻止程序员打破规则使用显式类型转换来删除constness。然后编译器(已被程序员殴打提交)将允许尝试修改指向的对象而没有任何抱怨。严格来说,这会导致未定义的行为,因此允许任何可以想象的结果,包括 - 可能 - 修改指向的对象。

【讨论】:

    【解决方案3】:

    如果没有,是否有(可移植的,如果可能的话)方法告诉编译器 doesnt_modify 不会修改其参数指向的内容?

    没有这样的方式。

    当涉及指针和引用函数参数时,编译器优化器难以优化。因为该函数的实现可以抛弃 constness 编译器假定 T const*T* 一样糟糕。

    因此,在您的示例中,在调用 doesnt_modify(n) 之后,它必须从内存中重新加载 *n

    2013 Keynote: Chandler Carruth: Optimizing the Emergent Structures of C++。它也适用于 C。

    在此处添加restrict 关键字不会改变上述内容。

    【讨论】:

      【解决方案4】:

      同时在指针类型参数上使用 restrict 限定符和在其目标类型上使用 const 限定符会导致编译器假定在指针对象的生命周期内没有通过其中包含的指针或从它派生的任何指针,将在该指针的生命周期内通过任何方式进行修改。它通常对不使用相关指针访问的存储区域没有任何说明。

      const restrict 对整个对象有影响的唯一情况是使用带有static 绑定的数组语法声明指针的情况。在这种情况下,行为只会在可以读取整个数组对象的情况下定义(不调用 UB)。由于读取在函数执行期间更改的数组对象的任何部分都会调用 UB,因此代码将被允许假设数组的任何部分都不能以任何方式更改。

      不幸的是,虽然知道函数的实际定义的编译器以:

      开头
      void foo(int const thing[restrict static 1]);
      

      有权假设在函数执行期间*thing 的任何部分都不会被更改,即使该对象可能是函数可以通过不是从thing 派生的指针访问的对象,事实上函数的原型包括这样的限定词不会强迫它的定义做同样的事情。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-14
        • 1970-01-01
        • 2018-03-16
        • 1970-01-01
        • 2014-05-12
        • 2014-04-29
        相关资源
        最近更新 更多