假设我有一个大数组,我计算索引并传递给第二个函数。举个简单的例子,例如:
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()中使用别名作为数组的一部分?
答案 0 :(得分:9)
(所有引文均指N1256,即C99加技术勘误(TC3)。)
restrict
的正式定义见§6.7.3.1。我引用下面最重要的子条款。 P
是restrict
- 限定类型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
的执行在任务之前结束。如果不满足这些要求,则行为未定义。
让我们看一下规则对于B2
中bar
array
部分内容的访问权限。我们从foo
开始,array
是在bar
的参数列表中声明的限制限定指针。为清楚起见,我将对foo
:
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
是限制合格,那么我们必须使用P
←b
,B
检查第四个项目符号点←foo
,P2
←array
,B2
←bar
。由于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
- 合格,因此不需要遵守任何线性条件。例如,以下foo
与bar
合并是合法的:
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