article提及:
受限制的指针可以从一个复制到另一个,以创建一个指针层次结构。但是,在C99标准中定义了一个限制。子指针不得与父指针位于同一块级范围内。在同一块级范围内复制受限指针的结果是不确定的。
然后提供了一个示例:
{
vector3* restrict position = &obj_a->position;
float* restrict position_x = &position->x; <-- UNDEFINED
{
float* restrict position_y = &position->y; <-- VALID
}
}
之后,还有另一个例子:
void
move( vector3* restrict velocity,
vector3* restrict position,
vector3* restrict acceleration,
float time_step,
size_t count,
size_t stride )
{
float* restrict acceleration_x = &acceleration->x;
float* restrict velocity_x = &velocity->x;
float* restrict position_x = &position->x;
我以为现在受限制的父指针position
与它的子指针position_x
在同一范围内,不是吗?根据我在本文开头引用的段落,这不是不允许的吗?
据我所知,该标准的作者首先确定了他们希望restrict
促进哪些优化,然后尝试编写规则,这些规则在所有可观察到的优化情况下均归为未定义行为。不幸的是,所编写的规则最终比允许的优化描述要复杂得多,而同时却无法以合理的甚至有意义的方式处理极端情况。例如,标准定义“基于”的方式,如果(p1 == p2) ? p3 : p4
和p1
,则表达式p1==p2
将基于“ p3!=p4
”,因为用指向相同数据的指针替换p1
会改变表达式的值。更奇怪的是,该标准可能被解释为(而clang和gcc以此方式解释!)是说左值表达式p[1]
并不总是基于p
。
如果不是查看“允许”执行的程序,而是要按顺序描述restrict
的实现,那将简化事情。使用至少可能从restrict
限定的指针派生并在其生存期内发生的左值进行的操作会相对于彼此以及该生存期的开始和结束进行排序,而在其生命周期中使用可能不会生成的左值进行操作从中得出的结果是相互关联的,并根据该生命周期的开始和结束进行排序,但是两组中的操作可以任意交织。如果在同一块中声明了两个指针,则它们的生命周期的末尾不会被排序,那么编译器将给出:
void test(int *p)
{
int * restrict p1 = p;
*p1 = 1;
int * restrict p2 = p1;
*p2 = 2;
}
可以将其转换为:
void test(int *p)
{
// Start lifetime of p1
int p1_temp = *p; // Copy everything that will be accessed via p1
int * restrict p1 = &p1_temp;
*p1 = 1;
// Start lifetime of p2
int p2_temp = *p1; // Copy everything that will be accessed via p2
int * restrict p2 = &p2_temp;
*p2 = 2;
// End lifetime of p2
*p1 = p2_temp; // Copy data back to *p1
// End lifetime of p1
*p = p1_temp; // Copy data back to *p
}
这会起作用,但是因为p1和p2的生存期未排序,所以可以对最后两个语句进行转置,从而破坏代码。
我认为标准的目的是使函数自变量的生存期超出任何其他自动对象的生存期,从而有效地表现出此类对象位于嵌套块内。但是,鉴于clang和gcc似乎试图利用该标准的“限制”定义的一些荒谬的极端情况,因此,期望它们在该标准甚至遥不可及的情况下做出明智的举动是危险的。