关于restrict限定指针与其他指针访问对象的标准合规性问询
核心问题
使用restrict关键字修饰的指针指向的对象是否绝对不允许通过其他指针访问?
C2011标准6.7.3.1相关规定
1 设D为普通标识符的声明,该声明将对象P指定为指向类型T的restrict限定指针。
2 若D出现在块内且无extern存储类,则B表示该块;若D出现在函数定义的参数声明列表中,则B表示关联块;否则B表示main函数的块(或独立环境中程序启动时调用的任意函数的块)。
3 在下文中,若在B执行期间、表达式E求值之前的某个序列点修改P使其指向原指向数组对象的副本会改变E的值,则称指针表达式E基于对象P。注意“基于”仅针对指针类型的表达式定义。
4 在B的每次执行期间,设L为任意左值,其地址&L基于P。若L被用于访问其指定的对象X的值,且X也被(通过任意方式)修改,则需满足以下要求:T不得是const限定类型;所有其他用于访问X值的左值的地址也必须基于P;就本条款而言,每次修改X的访问也应视为修改P……
测试代码
int main() { int A = 5; int* restrict P; P = &A; *P = 10; int *N = P; *N = 20; }
个人分析
*P是左值L,&*P是L的地址&L,显然P基于&L。当使用L(*P)访问对象X时,根据标准要求,所有其他访问X值的左值的地址必须基于P。代码中N被赋值为P的值,我认为N也基于P,因为修改P时N的值也会改变,因此通过*N访问X不属于未定义行为。
但此前学到的知识是:在声明restrict指针P的块的每次执行中,若P可直接或间接访问的对象被任意方式修改,则该块内对该对象的所有访问必须通过P进行,否则行为未定义。而我用了另一个普通指针*N访问X,因此产生以下疑问:
- 我的程序是否严格符合标准(不存在未定义行为)?
- 若程序合规,是否意味着restrict修饰的指针指向的对象可被其他普通指针访问?
- 若程序不合规,应如何理解C2011标准中的相关表述?
解答
1. 程序是否合规?
你的代码存在未定义行为。问题出在对“基于P”的理解错误:标准中定义的“表达式E基于P”是指修改P会改变E的值,但这里的N是一个独立指针,它的值是P的副本——修改P本身(比如让P指向另一个变量)并不会改变N的值,所以N并不基于P。
当你通过*P修改X(也就是A)后,又通过*N访问并修改X,此时*N的地址(即N)并不基于P,违反了标准6.7.3.1第4条的要求,因此行为未定义。
2. restrict指针指向的对象能否被其他指针访问?
不是绝对不允许,但有严格条件:所有访问该对象的左值的地址必须基于这个restrict指针。简单来说,其他指针必须是“依赖于restrict指针的衍生指针”——比如通过P+1这样的表达式得到的指针,这类表达式的值会随P的改变而改变,才符合“基于P”的定义。而像代码中直接复制P的值得到的独立指针N,并不满足这个条件。
3. 如何理解标准中的表述?
标准的核心意图是给编译器提供优化依据:当一个restrict指针P被声明后,编译器可以假设在块B的执行期间,所有对P指向对象的访问(包括读写)都只能通过基于P的指针表达式来进行。这样编译器就可以做诸如省略冗余加载、重排内存操作等优化,而不用担心有其他独立指针会干扰这个对象的内存状态。
你之前学到的“所有访问必须通过P进行”是一种简化表述,更准确的说法是“所有访问必须通过基于P的指针表达式进行”——直接复制P得到的独立指针不属于这个范畴,因为它和P后续的修改无关,编译器无法跟踪到它和P的关联。
内容的提问来源于stack exchange,提问作者user24723440

