如何解决将限制指针分配给非限制指针的语义是什么?
以下假设代码是否正确(注释中的假设是否成立)?还是有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
的值,因此x
和x
并不是基于&*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;
的关系。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。