限制指针的潜在未定义行为

时间:2015-08-08 12:17:22

标签: pointers compiler-optimization c99 undefined-behavior restrict-qualifier

这是四个代码片段。为什么保证(或不保证)此代码能够生成定义良好的行为?

受限制的“循环引用”:

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

1 个答案:

答案 0 :(得分: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; };
    

'要么'选项是预期的,以及现在列出的问题。

'要么'结构在语法上是有效的,但几乎没用;你可以创建它们,但它们不能真正掌握任何有用的信息。

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的第6.7.3节类型限定符(ISO / IEC 9899:2011)说:

  

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

     

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

第6.7.3.1节 restrict 的正式定义说:

  

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

     

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

     

3在下文中,指针表达式E被称为基于对象P if(在B执行的某个序列点,在E的评估之前1}})修改P以指向它先前指向的数组对象的副本将改变E的值。 137)注意''based' '仅针对具有指针类型的表达式定义。

     

4在B的每次执行期间,让L为基于&L P的左值。如果L用于访问其指定的对象X的值,并且X也被修改(通过任何方式),则以下要求适用:T不得具有合格资格。用于访问X值的每个其他左值也应具有基于P的地址。出于本子条款的目的,修改X的每次访问也应被视为修改P。如果P   被赋予指针表达式E的值,该指针表达式基于与块P2相关联的另一个受限指针对象B2,然后B2的执行将在B的执行,或B2的执行应在转让之前结束。如果不满足这些要求,则行为未定义。

     

5这里B的执行意味着程序执行的一部分对应于具有与B相关联的标量类型和自动存储持续时间的对象的生命周期。

     

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

这似乎允许你展示的代码。指针引用是基于'受限制的指针以及该部分允许的那些。