将非限制指针分配给限制指针

Assigning a non-restricted pointer to a restricted pointer

我最近正在实现一个以 restrict 指针作为参数的函数 (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 中看到错误地使用 restrict 会导致未定义的行为。特别是,我对“从一个受限指针到另一个受限指针的赋值是未定义的行为”这句话感到困惑。虽然我相信 calloc() 不会 return 受限指针,但我的程序是否没有未定义的行为?

是的,这是一个合格的程序。

这是规范的相关部分:

6.7.3 Type qualifiers

8 An object that is accessed through a restrict-qualified pointer has a special association with that pointer. This association, defined in 6.7.3.1 below, requires that all accesses to that object use, directly or indirectly, the value of that particular pointer.135) The intended use of the restrict qualifier (like the register storage class) is to promote optimization, and deleting all instances of the qualifier from all preprocessing translation units composing a conforming program does not change its meaning (i.e., observable behavior).

您没有通过任何其他非派生的指针访问 q 指针指向的内存。

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

1 Let D be a declaration of an ordinary identifier that provides a means of designating an object P as a restrict-qualified pointer to type T.

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

2 If D appears inside a block and does not have storage class extern, let B denote the block. If D appears in the list of parameter declarations of a function definition, let B denote the associated block. Otherwise, let B denote the block of main (or the block of whatever function is called at program startup in a freestanding environment).

所以B是定义函数的块。

3 In what follows, a pointer expression E is said to be based on object P if (at some sequence point in the execution of B prior to the evaluation of E) modifying P to point to a copy of the array object into which it formerly pointed would change the value of E. Note that "based" is defined only for expressions with pointer types.

q 仅在 *q++ 中使用,因此修改 P (q) 的指针表达式唯一会改变的是 q*q++ 里面的 q++。 (注意这个issue修改P指向一个copy的目的是为了让我们考虑如果P被改变但是它仍然指向会发生什么到相同的值,因为它们已被复制。换句话说,每个只涉及 P 指向的值的计算将保持不变;只有涉及 [=56 的特定值的事情=]P 将受此更改影响。)

4 During each execution of B, let L be any lvalue that has &L based on P. If L is used to access the value of the object X that it designates, and X is also modified (by any means), then the following requirements apply: T shall not be const-qualified. Every other lvalue used to access the value of X shall also have its address based on P. Every access that modifies X shall be considered also to modify P, for the purposes of this subclause. If P is assigned the value of a pointer expression E that is based on another restricted pointer object P2, associated with block B2, then either the execution of B2 shall begin before the execution of B, or the execution of B2 shall end prior to the assignment. If these requirements are not met, then the behavior is undefined.

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

在所示示例中,您不需要用 restrict 声明 q;它没有任何用处。用 restrict 声明 p 足以表明通过它修改的字节也不会通过 q.

访问

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