【问题标题】:Assigning a non-restricted pointer to a restricted pointer将非受限指针分配给受限指针
【发布时间】:2021-06-30 00:14:57
【问题描述】:

我最近正在实现一个以限制指针作为参数的函数 (my_copy()):

#include <stdio.h>
#include <stdlib.h>

void my_copy(int n, int * restrict p, int * restrict q) {
    if (q == NULL) {
        q = calloc(n, sizeof(int));
    }
    while(n-- > 0) {
        *p++ = *q++;
    }
    // Ignore memory leak for now
}

int main() {
    int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int b[10];
    // Copy a to b
    my_copy(10, b, a);
    for (int i = 0; i < 10; i++)
        printf("%d ", b[i]);
    printf("\n");
    // Zero a
    my_copy(10, a, NULL);
    for (int i = 0; i < 10; i++)
        printf("%d ", a[i]);
    printf("\n");
}

为了在my_copy() 中实现“默认值”,我将分配给受限指针q。但是,我在https://en.cppreference.com/w/c/language/restrict 中看到不正确地使用限制会导致未定义的行为。特别是,我对“从一个受限指针到另一个受限指针的赋值是未定义的行为”这句话感到困惑。虽然我相信calloc() 不会返回受限指针,但我的程序是否没有未定义的行为?

【问题讨论】:

    标签: c restrict-qualifier


    【解决方案1】:

    是的,这是一个符合标准的程序。

    这是规范的相关部分:

    6.7.3 类型限定符

    8 通过限制限定指针访问的对象与该指针具有特殊关联。这种关联,在下面的 6.7.3.1 中定义,要求对该对象的所有访问直接或间接使用该特定指针的值。135) 限制限定符的预期用途(如寄存器存储类)是为了促进优化,并且从构成符合程序的所有预处理翻译单元中删除限定符的所有实例不会改变其含义(即,可观察的行为)。

    您没有通过任何其他不是从它派生的指针访问由q 指针指向的内存。

    【讨论】:

      【解决方案2】:

      restrict 的正式定义在 6.7.3.1 中。第 1-4 段与此相关:

      1 令 D 是一个普通标识符的声明,它提供了一种将对象 P 指定为类型 T 的限制限定指针的方法强>。

      Dint * restrict q;它提供了一种将q 指定为类型int 的限制限定指针的方法。 PqTint

      2 如果 D 出现在块内并且没有存储类 extern,则让 B 表示该块。如果 D 出现在函数定义的参数声明列表中,则让 B 表示关联的块。否则,让 B 表示 ma​​in 的块(或在独立环境中程序启动时调用的任何函数的块)。

      所以 B 是定义函数的块。

      3 在下文中,指针表达式 E 被称为基于对象 P if(在执行 B 的某个序列点strong> 在评估 E 之前)修改 P 以指向它以前指向的数组对象的副本会改变 E 的值强>。请注意,“基于”仅针对具有指针类型的表达式定义。

      q 仅在 *q++ 中使用,因此修改 P (q) 的唯一指针表达式将更改为 q*q++ 内的 q++。 (注意这个问题关于修改 P 以指向一个副本的目的是为了让我们可以考虑如果 P 被更改但它仍然指向相同的值会发生什么,因为换句话说,每一个只涉及 P 指向的值的计算都将保持不变;只有涉及 P 的特定值的事情才会受到影响这种变化。)

      4 在每次执行 B 期间,让 L 为基于 P 具有 &L 的任何左值。如果 L 用于访问它指定的对象 X 的值,并且 X 也被修改(通过任何方式),那么以下要求适用: T 不应是 const 限定的。用于访问 X 值的每个其他左值也应具有基于 P 的地址。就本条而言,修改 X 的每个访问都应被视为修改 P。如果 P 被分配一个指针表达式 E 的值,该指针表达式基于另一个受限指针对象 P2,与块 B2,则 B2 的执行应在 B 的执行之前开始,或者 B2 的执行应在分配之前结束.如果不满足这些要求,则行为未定义。

      *q++ 是一个左值,其地址 &amp;*q++ = q++ 取决于 P (q)。此左值 L 用于访问原始传递的内存或分配的内存中的各种字节 X。但是,这些字节 X 都没有被修改。 (我们知道它们不会与例程中修改的任何字节重叠,那些由p 指向的字节,因为p 也用restrict 声明。)所以上面列出的“以下要求”不适用。

      在所示示例中,您无需将q 声明为restrict;它毫无用处。用restrict 声明p 足以表明通过它修改的字节也不会通过q 访问。

      这就是上面定义的主旨:如果你通过一个基于受限指针的左值L修改了某个对象X,你也不会访问那个X 通过另一个不是从同一个受限指针派生的指针。因此,如果您将某个非限制指针的值分配给q,并且永远不要通过基于q 的某个表达式修改内存,或者永远不要在@987654348 的生命周期内通过某些不基于q 的表达式访问修改后的内存@,那你就没事了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-04-15
        • 2014-12-21
        • 2013-12-10
        • 2014-05-13
        • 2016-03-03
        • 1970-01-01
        • 1970-01-01
        • 2014-08-23
        相关资源
        最近更新 更多