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

时间:2010-10-04 16:19:13

标签: 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);
}

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

2 个答案:

答案 0 :(得分:9)

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

restrict的正式定义见§6.7.3.1。我引用下面最重要的子条款。 Prestrict - 限定类型T的指针,其范围为块B。如果指针表达式E取决于P本身的值,而不是P指向的值,则表示基于 P 到。

  

B的每次执行期间,让L为基于P的&L的左值。如果L用于访问对象的值{{ 1}}它指定,X也被修改(通过任何方式),然后适用以下要求:

     
      
  • X不得为const限定。
  •   
  • 用于访问T值的每个其他左值也应根据X设置其地址。
  •   
  • 为了本子条款的目的,每次修改P的访问权也应被视为修改X
  •   
  • 如果为P分配了指针表达式P的值,该指针表达式基于与块E关联的另一个受限指针对象P2,则执行B2应在B2执行之前开始,或B的执行在任务之前结束。
  •   
     

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


让我们看一下规则对于B2bar array部分内容的访问权限。我们从foo开始,array是在bar的参数列表中声明的限制限定指针。为清楚起见,我将对foo

的参数进行alpha转换
void foo(float* b, float c, unsigned int n) { /*modify b[i]*/ }

array指向的存储也通过b修改。第二个项目符号点正常,因为&array[i*n]等同于array+(i*n)(参见§6.5.3.2)。

如果b是限制合格,那么我们必须使用PbB检查第四个项目符号点←fooP2arrayB2bar。由于B嵌套在B2内(函数的行为就像在此处内联,参见§6.7.3.1.11),因此满足第一个条件。第三个项目符号点(b[i]foo的访问权限)也有一个实例,这不是问题。

b不受限制。根据§6.3.2.3.2,“对于任何限定符 q ,指向非 q - 限定类型的指针可能会转换为指向的指针q - 该类型的合格版本;存储在原始和转换指针中的值应比较相等“。因此,从array+(i*n)b的转换是明确定义的,具有明显的含义,因此定义了程序的行为。此外,由于b不是restrict - 合格,因此不需要遵守任何线性条件。例如,以下foobar合并是合法的:

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

已添加:要解决您在特定问题“在bar()中将数组部分地址传递给foo()”,这不是问题:restrict适用于指针,而不是数组,你可以对它进行算术运算(项目符号第2点)。

答案 1 :(得分:-2)

不,限制意味着数组不能对任何内容进行别名,因此您可以在不违反规则的情况下将内容传递给bar