0

这里有四个代码片段。为什么保证(或不保证)这段代码会产生明确定义的行为?

受限的“循环引用”:

struct B;
struct A { struct B *restrict b1, *restrict b2; };
struct B { struct A *restrict a1, *restrict a2; };

外向内赋值:

void f1(int *restrict p) {
  {
    int *restrict p2 = p;
    *p2 = 0;
  }
}

传递给函数:

static inline void f2helper(int *restrict p) {
  *p = 0;
}
void f2(int *restrict p) {
  f2helper(p);
}

指针算术循环:

void f3(int *restrict p, size_t s) {
  int * p2 = p + s;
  while (p2 > p)
    *--p2 = 0;
}
4

1 回答 1

1

受限的“循环引用”:

最初,问题包含保证不会编译的代码:

struct B;
struct A { struct B *restrict b1, b2; };
struct B { struct A *restrict a1, a2; };

第一个结构等价于:

struct A { struct B *restrict b1; struct B b2; };

并且您不能拥有不完整类型的元素(尽管您可以拥有指向不完整类型的受限指针),因此b2无效。


指出了这一点,并提供了两种可能性:

  • '任何一个':

    struct B;
    struct A { struct B *restrict b1, *restrict b2; };
    struct B { struct A *restrict a1, *restrict a2; };
    
  • '或者':

    struct B;
    struct A { struct B *restrict b1; int b2; };
    struct B { struct A *restrict a1; int a2; };
    

'either' 选项是预期的,以及问题现在列出的内容。

“任何一种”结构在语法上都是有效的,但几乎没用;你可以创建它们,但它们不能真正保存任何有用的信息。

struct A a1, a2;
struct B b1, b2;

a1 = (struct A){ &b1, &b2 };
a2 = (struct A){ &b2, &b1 };
b1 = (struct B){ &a1, &a2 };
b2 = (struct B){ &a2, &a1 };

你真的不能用这些做很多事情。

“或”结构在语法上也是有效的,并且可能有一些用途,尽管很难看到它们非常有用。

外向内赋值:

传递给函数:

指针算术循环:

据我所知,所有三组功能都很干净。

C11 (ISO/IEC 9899:2011) 的第 6.7.3 节类型限定符说:

8 通过限制限定指针访问的对象与该指针具有特殊关联。这种关联,在下面的 6.7.3.1 中定义,要求对该对象的所有访问都直接或间接地使用该特定指针的值。135)限制限定符(如寄存器存储类)的预期用途是促进优化,并且从构成符合程序的所有预处理翻译单元中删除限定符的所有实例不会改变其含义(即,可观察的行为)。

135)例如,将返回的值分配给malloc单个指针的语句在分配的对象和指针之间建立了这种关联。

第 6.7.3.1 节的正式定义restrict说:

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

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

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

4 在每次执行期间B,设L为任何&L基于 的左值P。如果L用于访问X它指定的对象的值,并且X也被修改(通过任何方式),则适用以下要求:T不应该是 const 限定的。用于访问 的值的每个其他左值X也应具有基于 的地址P。就本条而言,修改的每个访问也X应被视为修改P。如果P 分配了一个指针表达式的值,该指针表达式E基于另一个P2与块关联的受限指针对象B2,则要么执行B2应在 的执行之前开始B,或 的执行B2应在转让之前结束。如果不满足这些要求,则行为未定义。

5 这里的执行是B指与标量类型和与 关联的自动存储持续时间的对象的生命周期相对应的程序执行部分B

137)换句话说,E依赖于P自身的值,而不是通过间接引用的对象的值P。例如,如果 identifierp具有类型 ( int **restrict),则指针表达式pp+1是基于由 指定的受限指针对象p,但指针表达式*pp[1]不是。

这似乎允许您显示的代码。指针引用“基于”受限制的指针,这是该部分允许的。

于 2015-08-08T18:09:43.740 回答