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

发布于 2024-09-26 04:48:02 字数 454 浏览 1 评论 0原文

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

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 中的数组的一部分使用别名()?

Suppose I have large array which I calculate an index into and pass to a second function. As a simple example, something like:

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);
}

Is this breaking the rules for restrict in bar() where you pass the address of part of the array to foo(), even if you never really use the alias for a part of the array within bar()?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

江城子 2024-10-03 04:48:02

(所有引用均参考 N1256,即C99 加上技术勘误 (TC3)。)

restrict 的正式定义在 §6.7.3.1 中给出。我引用下面最重要的条款。 P 是一个 restrict 限定的指针,指向类型 T,其范围是块 B。如果指针表达式 E 取决于 P 本身的值,则称其基于 P,而不是P 指向的值。

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

  • T 不应是 const 限定的。
  • 用于访问 X 值的每个其他左值也应具有基于 P 的地址。
  • 就本子条款而言,修改 X 的每次访问都应被视为修改 P
  • 如果为 P 分配了一个指针表达式 E 的值,该表达式基于另一个与块 关联的受限指针对象 P2 >B2,则 B2 的执行应在 B 执行之前开始,或者 B2 的执行应先于到作业。

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


让我们看看规则对于访问 foobararray 部分有什么规定。我们从 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 是限制限定的,那么我们必须使用 Pb 检查第四个要点, BfooP2数组B2bar< /代码>。由于 B 嵌套在 B2 内(此处函数的行为如同内联,请参阅第 6.7.3.1.11 节),因此满足第一个条件。还有第三个要点的一个实例(在 foo 中访问 b[i]),这不是问题。

但是 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);
}

ADDED:为了解决您的特定问题“在 bar() 中传递数组的一部分的地址到 foo()”,这不是问题:restrict 适用于指针,而不是数组,并且您可以对其执行算术(要点 2)。

(All citations refer to N1256, which is C99 plus technical corrigenda (TC3).)

The formal definition of restrict is given in §6.7.3.1. I quote the most important subclause below. P is a restrict-qualified pointer to type T whose scope is a block B. A pointer expression E is said to be based on P if it depends on the value of P itself, not the value that P points to.

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:

  • T shall not be const-qualified.
  • Every other lvalue used to access the value of X shall also have its address based on P.
  • Every access that modifies X shall be considered also to modify P, for the purposes of this subclause.
  • If P is assigned the value of a pointer expression E that is based on another restricted pointer object P2, associated with block B2, then either the execution of B2 shall begin before the execution of B, or the execution of B2 shall end prior to the assignment.

If these requirements are not met, then the behavior is undefined.


Let's look at what the rules have to say about accesses to parts of bar's array in foo. We start with array, a restrict-qualified pointer declared in the parameter list of bar. For clarity, I will alpha-convert the parameters of foo:

void foo(float* b, float c, unsigned int n) { /*modify b[i]*/ }

The storage pointed to by array is also modified through b. That's ok with the second bullet point as &array[i*n] is equivalent to array+(i*n) (see §6.5.3.2).

If b was restrict-qualified, then we would have to check the fourth bullet point with P​←b, B​←foo, P2​←array, B2​←bar. Since B is nested inside B2 (functions behave as if inlined here, see §6.7.3.1.11), the first condition is met. There is also one instanciation of the third bullet point (the access to b[i] in foo) which is not a problem.

However b is not restrict-qualified. According to §6.3.2.3.2, “For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type; the values stored in the original and converted pointers shall compare equal”. Therefore the conversion from array+(i*n) to b is well-defined and has the obvious meaning, so the program's behavior is defined. Furthermore, since b is not restrict-qualified, it doesn't need to obey any linearity condition. For example, the following foo is legal in combination with bar:

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

ADDED: To address your specific concern “in bar() where you pass the address of part of the array to foo()”, this is a non-issue: restrict applies to the pointer, not the array, and you can perform arithmetic on it (bullet point 2).

油饼 2024-10-03 04:48:02

不,restrict 意味着 array 不能给任何东西起别名,所以你可以将东西传递给 bar 而不会违反规则

No, restrict means that array can't alias anything, so you can pass stuff to bar without breaking the rules

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文