将非受限指针分配给受限指针 6.7.3 类型限定符

问题描述

我最近正在实现一个函数 (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() 不会返回受限制的指针,但我的程序是否没有未定义的行为?

解决方法

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

这是规范的相关部分:

6.7.3 类型限定符

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

您没有访问 q 指针指向的内存,而任何其他指针都不是从它派生出来的。

,

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的值强>。请注意,“based”仅针对具有指针类型的表达式定义。

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

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

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

在显示的示例中,您不需要使用 q 声明 restrict;它没有任何意义。用 p 声明 restrict 足以表明通过它修改的字节也不会通过 q 访问。

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

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...