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

问题描述

以下假设代码是否正确(注释中的假设是否成立)?还是有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?

不。限制规则覆盖了一个块 B ,对于在函数参数中声明的具有限制条件的指针,该块是定义函数的块。该区块 B 中的该区块是 B 的一部分。

restrict的定义在C 2018 6.7.3.1中给出。第1段说:

D 是普通标识符的声明,该声明提供了一种方法来指定对象 P 作为类型为 T 的受限制的指针>。

int * restrict y是普通标识符“ y”的声明 D ,并且y指定对象 P 指向int(类型 T )的限制限定指针。

第2段说:

…如果 D 出现在函数定义的参数声明列表中,则让 B 表示关联的块…

所以 B 是定义功能的块。

第3段说:

接下来,如果(在执行 B 的某个顺序点上)指针表达式 E 被称为基于对象 P >在评估 E 之前)修改 P 以指向其先前指向的数组对象的副本将更改 E 。)请注意,“基于”仅针对具有指针类型的表达式定义。

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

第4段说:

在每次执行 B 时,将 L 设为具有&L (基于 P )的左值。如果使用 L 来访问其指定的对象 X 的值,并且还修改了 X (通过任何方式),则适用以下要求:…用于访问 X 值的所有其他左值也应基于 P

y是基于*a具有&L 的左值 L ,因为y&*a,我们知道a是基于a的。

然后,在y中,此 L 用于访问其指定的对象,并对该对象进行了修改。因此,必须满足以下要求:用于访问对象的所有其他左值也应基于*a = 123;进行寻址。

因此,如果y也访问了*x指定的对象,那将违反上面的要求,因为*a不是基于&*x的。 y作为单独的参数传递,并且更改x的值不会更改y的值,因此xx并不是基于&*x

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

,

如果存储到非限制指针的值“基于”限制指针的值,则使用该值的操作将被视为对原始指针执行的操作。在一个指针或左值绝对基于限制限定的指针而另一个绝对不基于限制的指针的情况下,这将非常直观地解决。不幸的是,不是使用简单的规则来定义何时指针“绝对基于P”或“绝对不基于P”,而是允许指针不属于任一类别(它们可能基于P,但可以(事实证明并非如此),该标准使用了更为复杂的规则,包括荒谬,不可行和模棱两可的案例。如果编译器认识到,即使在标准不要求使用拐角的情况下,以清晰明显的方式由限制限定的指针形成的指针值也应视为基于该指针,则这不是问题。 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不会导致对同一地址或不同地址的赋值,而是使赋值中使用的指针“基于{ {1}}”,对p的这种更改将需要更改用于分配的地址。 clang和gcc都认为完全阻止执行赋值不会“更改”该赋值中使用的地址,因此他们无法将赋值p中使用的指针识别为具有任何指针。与*p = 2;的关系。

相关问答

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