这是对限制指针的无效使用吗?

ars*_*enm 6 c c99 restrict-qualifier

假设我有一个大数组,我计算索引并传递给第二个函数.举个简单的例子,例如:

void foo(float* array, float c, unsigned int n)
{
    for (unsigned int i = 0; i < n; ++i)
        array[i] *= c;
}

void bar(float* restrict array, float* restrict array2, unsigned int m, unsigned int n)
{
    for (unsigned int i = 0; i < m; ++i)
        foo(&array[i * n], array2[i], n);
}
Run Code Online (Sandbox Code Playgroud)

这是否打破了bar()中限制的规则,你将数组的一部分地址传递给foo(),即使你真的没有在bar()中使用别名作为数组的一部分?

Gil*_*il' 9

(所有引文均指N1256,即C99加技术勘误(TC3).)

正式定义restrict见§6.7.3.1.我引用下面最重要的子条款.P是一个restrict限定指向类型T的指针,其范围是一个块B.指针表达式E被认为是基于 P它是否取决于它P自身的值,而不是P指向的值.

在每次执行期间B,L设为任何&L基于P的左值.如果L用于访问X它指定的对象的值,并且X也被修改(通过任何方式),则以下要求适用:

  • T 不得具有合格资格.
  • 用于访问值的每个其他左值X也应该具有基于的地址P.
  • 出于本子条款的目的,每次修改的访问也X应被视为修改P.
  • 如果P指定了一个指针表达式的值,该指针表达式E基于P2与块相关联的另一个受限指针对象,B2那么执行B2应该在执行之前开始B,或者执行B2应该在赋值之前结束.

如果不满足这些要求,则行为未定义.


让我们来看看什么规则不得不说访问的部分bar的array在foo.我们首先array在参数列表中声明一个限制限定的指针bar.为清楚起见,我将alpha转换参数foo:

void foo(float* b, float c, unsigned int n) { /*modify b[i]*/ }
Run Code Online (Sandbox Code Playgroud)

指向的存储array也通过修改b.对于第二个要点而言,这&array[i*n]是可以的array+(i*n)(参见§6.5.3.2).

如果b是限制合格的话,那么我们必须用P← b,B← foo,P2← array,B2← 检查第四个子弹点bar.由于B嵌套在内部B2(函数的行为就像在此处内联,参见§6.7.3.1.11),因此满足第一个条件.还有一个第三个子弹点(对b[i]in 的访问foo)的实例,这不是问题.

但是b不受限制.根据§6.3.2.3.2,"对于任何限定符q,指向非q限定类型的指针可以转换为指向该类型的q-限定版本的指针; 存储在原始和转换指针中的值应比较相等".因此,从转换array+(i*n)到b是明确的,具有明显的意义,所以程序的行为被定义.此外,由于b不restrict-qualified,它不需要服从任何线性条件.例如,以下foo内容与以下内容合法bar:

void qux(float *v, float *w) {
    v[0] += w[0];
}
void foo(float* b, float c, unsigned int n)
{
    qux(b,b);
}
Run Code Online (Sandbox Code Playgroud)

增加:为了解决您在特定问题"在bar()中将数组的一部分的地址传递给foo()",这是一个非问题:restrict适用于指针,而不是数组,并且您可以执行算术运算它(子弹点2).