受限的“循环引用”:
最初,问题包含保证不会编译的代码:
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
出现在块内并且没有存储类extern
,B
则表示该块。如果D
出现在函数定义的参数声明列表中,letB
表示关联的块。否则,让B
表示块main
(或在独立环境中程序启动时调用的任何函数的块)。
3 在下文中,如果(在对 的求值之前的执行中的某个序列点)修改为指向它先前指向的数组对象的副本将改变值,则指针表达式E
被称为基于对象的。137)请注意,“基于”仅针对具有指针类型的表达式定义。P
B
E
P
E
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
),则指针表达式p
和p+1
是基于由 指定的受限指针对象p
,但指针表达式*p
和p[1]
不是。
这似乎允许您显示的代码。指针引用“基于”受限制的指针,这是该部分允许的。