将限制指针分配给非限制指针的语义是什么?

What are the semantics of assigning restrict pointer to a non-restrict one?

下面的假设代码是否正确(注释中的假设是否成立)?还是有UB?

#define N 1 // what if it's 0?

void foo(int *x, int * restrict y) {
  *x = 42;
  *y = 0;
  // compiler can assume *x is still 42.
  {
    int *a = y + N;
    *a = 123;
    // compiler can no longer assume *x is 42.
  } // does the new scope matter in this case?
}

// compiler can no longer assume *x is 42.

我认为这种说法没有根据。

// does the new scope matter in this case?

没有。 restrict 的规则涵盖块 B,对于在函数参数中声明的 restrict-qualified 指针,它是定义函数的块。该块 B 中的该块是 B.

的一部分

restrict的定义在C 2018 6.7.3.1中给出。第 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.

int * restrict y是普通标识符“y”的声明Dy指定一个对象 P 是指向 int 的 restrict-qualified 指针(类型 T)。

第 2 段说:

… If D appears in the list of parameter declarations of a function definition, let B denote the associated block…

所以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.

因此,在 int *a = y + N; 之后,a 基于 y,因为在 a 初始化之前修改 y 会更改 a,即使 y 已更改为指向数组的副本。

第 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: … Every other lvalue used to access the value of X shall also have its address based on P

*a 是左值 L,它具有基于 y&L,因为 &*aa,我们知道 a 是基于 y.

然后,在*a = 123;中,这个L用来访问它指定的对象,修改那个对象。所以要求必须适用:用于访问对象的每个其他左值也应根据 y.

进行寻址

所以,如果*x也访问了*a指定的对象,那就违反了上面的要求,因为&*x不是基于yx作为单独的参数传递,改变y的值不会改变x的值,所以x&*x不是基于y.

由于编译器有权期望满足要求,因此可以假定 *a = 123; 不会更改 *x

如果存储到 non-restrict 指针的值“基于”限制指针的值,则使用该值的操作被认为是对原始值执行的操作。在一个指针或左值绝对基于 restrict-qualified 指针而另一个绝对不是的情况下,这将非常直观地解决。不幸的是,当一个指针“绝对基于 P”或“绝对不基于 P”时,并没有使用简单的规则,而是允许不属于任何一类的指针(它们可能基于 P,但可以'被证明是),该标准反而使用更复杂的规则,其中包含荒谬、不可行和模棱两可的极端情况。如果编译器认识到由 restrict-qualified 指针以清晰和明显的方式形成的指针值应该被视为基于该指针,即使在标准不要求的极端情况下,这也不是问题, 但 clang 和 gcc 不会那样处理事情。

例如,给定:

int x[1];
int foo(int *restrict p)
{
  *p = 1;
  if (p == x)
  {
    *p = 2;
  }
  return *p;      
}

赋值 *p = 2; 中使用的指针是基于 restrict p 的,这在直觉上似乎很明显,但是标准的措辞方式使它变得模棱两可。用 *p 的副本地址替换 p 既不会导致对同一地址执行赋值,也不会导致对不同地址执行赋值,但赋值中使用的指针“基于 p",这样对 p 的更改将需要更改用于分配的地址。 clang 和 gcc 都认为完全阻止执行赋值不会“更改”该赋值中使用的地址,因此它们不会将赋值 *p = 2; 中使用的指针识别为具有任何与 restrict p.

的关系