将限制指针分配给非限制指针的语义是什么?
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
”的声明D,y
指定一个对象 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,因为 &*a
是 a
,我们知道 a
是基于 y
.
然后,在*a = 123;
中,这个L用来访问它指定的对象,修改那个对象。所以要求必须适用:用于访问对象的每个其他左值也应根据 y
.
进行寻址
所以,如果*x
也访问了*a
指定的对象,那就违反了上面的要求,因为&*x
不是基于y
。 x
作为单独的参数传递,改变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
.
的关系
下面的假设代码是否正确(注释中的假设是否成立)?还是有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
”的声明D,y
指定一个对象 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,因为 &*a
是 a
,我们知道 a
是基于 y
.
然后,在*a = 123;
中,这个L用来访问它指定的对象,修改那个对象。所以要求必须适用:用于访问对象的每个其他左值也应根据 y
.
所以,如果*x
也访问了*a
指定的对象,那就违反了上面的要求,因为&*x
不是基于y
。 x
作为单独的参数传递,改变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
.