6

大多数限制的定义都表示,程序员向编译器承诺,在指针的生命周期内,指针是访问对象的唯一方式。这允许编译器优化输出,因为它知道它只能由一个指针访问,因此只能由它更改。如果我理解正确,这通常意味着程序不必重新加载指针指向的值。

如果这是正确的,那么当限制关键字应该可用时应该有一些例外,即使它违背了应该如何使用它的意图。

想到的一件事是当指针指向的数据在指针的生命周期内从未真正改变时。在这种情况下,即使指针指向相同的位置,也无需重新加载数据,因为它们在指针的生命周期内不会改变。例如:

int max(int *restrict a, int *restrict b) {
  return((*a > *b) ? *a : *b);
}

int main(void) {
  int num = 3;
  int max = max(&num, &num);
}

这是对限制的有效使用,即使它违背了它应该如何使用?像这样使用限制关键字会导致未定义的行为吗?

4

2 回答 2

3

您有时可以使用限制限定的指针来访问与其他指针相同的对象,但前提是未修改指向的对象。这是 C 2011 (N1570) 6.7.3.1 第 1-3 段和第 4 段的第一部分,其中穿插了它们如何应用于问题中的代码。

6.7.3.1 限制的正式定义

1 令D是一个普通标识符的声明,它提供了一种将对象P指定为类型T的限制限定指针的方法。

这样int * restrict a的声明D也是如此。当max用 调用时max(&num, &num);,对象Pnum(或更正式地,由 命名的对象num),而Tint。同样,int * restrict b是另一个这样的声明。

2 如果D出现在一个块内并且没有存储类 extern,则让B表示该块。如果D出现在函数定义的参数声明列表中,则让B表示关联的块。否则,让B表示main块(或在独立环境中程序启动时调用的任何函数块)。

这些声明出现在函数定义的参数声明中,所以B是函数定义的块,即max.

3 在下文中,指针表达式E被称为基于对象P如果(在执行B的某个序列点处,在评估E之前)修改P以指向它之前所在的数组对象的副本指向会改变 E.137 的值)请注意,“基于”仅针对具有指针类型的表达式定义。

该函数max包含指针表达式ab,每个两次,因此它们都是指针表达式E的实例。这些表达式分别取决于参数ab,因为如果我们更改a为指向 的副本num而不是指向num,那么a(显然)将具有不同的值,对于b. (虽然num是一个标量对象,但它就像一个包含单个元素的数组,用于指针运算。)

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

在 的执行过程中max,左值*a的地址 ( &*a,即a) 基于P ( a),因此左值*aL的一个实例。此左值用于访问numnum对象X的实例也是如此。但是num在执行过程中永远不会被修改max。因此,以下要求不适用。类似地,左值*b指的num是在执行期间从未修改过的对象 () max

因此, 中的代码max不违反 的要求restrict,其行为由 C 标准定义。

于 2013-08-05T14:45:53.650 回答
3

正如 Eric 在现在已经消失的评论中所说的那样,其中的关键短语是:C99 draft standard 6.7.3.1 Formal definition of restrict

`If… X is also modified…`

此解释在以下示例中得到支持6.7.3.1/10

void h(int n, int * restrict p, int * restrict q, int * restrict r)
{
  int i;
  for (i = 0; i < n; i++)
    p[i] = q[i] + r[i];
}

以及带有代码示例的以下注释:

说明如何通过两个受限指针对未修改的对象进行别名。特别是,如果 a 和 b 是不相交的数组,则 h(100, a, b, b) 形式的调用具有已定义的行为,因为数组 b 在函数 h 内没有被修改。

因此,您的具体示例似乎是defined behavior因为您没有修改aor b

于 2013-08-05T14:45:34.810 回答