蓝天

c99 增加的restrict关键字

c99中新增加了一个类型定义,就是restrict。
restrict的定义是It can be applied only to pointers, and it indicates that a pointer is the sole initial means of accessing a data object.
我不知道确切应该怎么翻译,大意是restrict只对指针有用,它声明一个指针是唯一初始化访问一个数据对象。
比如,按照书上的例子,
int ar[10];

int * restrict restar = (int *) malloc(10 * sizeof(int));

int * par = ar;

for (n = 0; n < 10; n++)

{

      par[n] += 5;

      restar[n] += 5;

      ar[n] *= 2;

      par[n] += 3;

      restar[n] += 3;

}

restar指针是restrict类型,par指针就不是,因为par即没有初始化也不是唯一访问ar数组的变量。
那么,上面的程序,因为restar是唯一反问数据块的指针,所以编译器可以对它优化为一条语句,
restar[n] += 8;     /* ok replacement */
而par就不可以,
par[n] += 8;      / * gives wrong answer */
One of the new features in the recently approved C standard C99, is the restrict pointer qualifier. This qualifier can be applied to a data pointer to indicate that, during the scope of that pointer declaration, all data accessed through it will be accessed only through that pointer but not through any other pointer. The 'restrict' keyword thus enables the compiler to perform certain optimizations based on the premise that a given object cannot be changed through another pointer. Now you're probably asking yourself, "doesn't const already guarantee that?" No, it doesn't. The qualifier const ensures that a variable cannot be changed through a particular pointer. However, it's still possible to change the variable through a different pointer. For example:

 

    void f (const int* pci, int *pi;); // is *pci immutable?
    {
      (*pi)+=1; // not necessarily: n is incremented by 1
       *pi = (*pci) + 2; // n is incremented by 2
    }
    int n;
    f( &n, &n);

In this example, both pci and pi point to the same variable, n. You can't change n's value through pci but you can change it using pi. Therefore, the compiler isn't allowed to optimize memory access for *pci by preloading n's value. In this example, the compiler indeed shouldn't preload n because its value changes three times during the execution of f(). However, there are situations in which a variable is accessed only through a single pointer. For example:

 

    FILE *fopen(const char * filename, const char * mode);

The name of the file and its open mode are accessed through unique pointers in fopen(). Therefore, it's possible to preload the values to which the pointers are bound. Indeed, the C99 standard revised the prototype of the function fopen() to the following:

 
    /* new declaration of fopen() in <stdio.h> */
    FILE *fopen(const char * restrict filename,
                          const char * restrict mode);

Similar changes were applied to the entire standard C library: printf(), strcpy() and many other functions now take restrict pointers:

 
    int printf(const char * restrict format, ...);
    char *strcpy(char * restrict s1, const char * restrict s2);

C++ doesn't support restrict yet. However, since many C++ compilers are also C compilers, it's likely that this feature will be added to most C++ compilers too.


  
  
  
所以,在没有restrict的时候,编译器需要考虑上面的出错情况,而无法优化程序。

restrict也可以用在函数的指针参数前面,它表示在函数里没有其他标识符会修改指针所指的数据块,编译器可以优化函数。

在C99中新增了类型修饰符(qualifier) restrict 在网上找来找去,看到介绍它的不多,就把看到的一些介绍做个总结。
传说中下面是是书中的原话,到底是什么书却没人说:
========================8<====================================
restrict这种修饰符只适用于指针.
由restrict修饰的指针是最初唯一对指针所指向的对象进行存取的办法,
仅当第二个指针基于第一个时,才能对对象进行存取.
因此,对对象的存取都限定于基于有restrict修饰的指针表达式中.
由restrict修饰的指针主要被用做函数指针,或者指向由malloc()分配的内存变量.
restrict数据类型不改变程序的语义.
=======================8<=======================================
感觉说的有的乱,可能是我的理解能力不够吧。然后在CSDN看到了这个:
========================8<====================================

restrict是C99版新增加的关键字!   如下:

       C99     中新增加了     restrict     修饰的指针: 由     restrict     修饰的指针是最初唯一对指针所指向的对象进行存取的方法,仅当第二个指针基于第一个时,才能对对象进行存取。对对象的存取都限定于基于由     restrict     修饰的指针表达式中。

       由     restrict     修饰的指针主要用于函数形参,或指向由     malloc()   分配的内存空间。restrict   数据类型不改变程序的语义。    编译器能通过作出     restrict   修饰的指针是存取对象的唯一方法的假设,更好地优化某些类型的例程。

       [典型例子]     memcpy()     在     C99     中,restrict     可明确用于     memcpy()     的原型,而在     C89     中必须进行解释。     void     *memcpy (void     *restrict     str1,     const     void     *restrict     str2,     size_t     size);     /*   通过使用     restrict     修饰     str1     和     str2     来保证它们指向不重叠的对象     */   

=======================8<=======================================
很多人说这个关键字主要是用来加强编译器优化的,理由也很简单:“由restrict修饰的指针是最初唯一对指针所指向的对象进行存取的办法,仅当第二个指针基于第一个时,才能对对象进行存取.”这样下面的代码就可以被很好的优化,

void fcpy(float *restrict a, float *restrict b,
       float *restrict aa, float *restrict bb, int n)
{
int i;
for(i = 0; i < n; i++) {
      aa[i]=a[i];
      bb[i]=b[i];
}
}

这意味着这些拷贝循环能够“并行”(in parallel),由于 例如 aa != b
但还是喜欢下面的例子,从它可以看出restrict不仅仅可以被用来加强编译器的优化,还是解决我们代码中存在的隐患。
=====================8<================================
'Restrict' Pointers

One of the new features in the recently approved C standard C99, is the restrict pointer qualifier. This qualifier can be applied to a data pointer to indicate that, during the scope of that pointer declaration, all data accessed through it will be accessed only through that pointer but not through any other pointer. The 'restrict' keyword thus enables the compiler to perform certain optimizations based on the premise that a given object cannot be changed through another pointer. Now you're probably asking yourself, "doesn't const already guarantee that?" No, it doesn't. The qualifier const ensures that a variable cannot be changed through a particular pointer. However, it's still possible to change the variable through a different pointer. For example:
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->

  void f (const int* pci, int *pi;); // is *pci immutable?
    {
      (*pi)+=1; // not necessarily: n is incremented by 1
       *pi = (*pci) + 2; // n is incremented by 2
    }
    int n;
    f( &n, &n);//
增加restrict关键字是因为这里会出问题
,
//
如果对两个参数都使用了restrict关键字,那么这里编译时会报错,因为一

//
个地址可以通过两个指针访问了
<!--[if !supportLineBreakNewLine]-->

<!--[endif]-->

In this example, both pci and pi point to the same variable, n. You can't change n's value through pci but you can change it using pi. Therefore, the compiler isn't allowed to optimize memory access for *pci by preloading n's value. In this example, the compiler indeed shouldn't preload n because its value changes three times during the execution of f(). However, there are situations in which a variable is accessed only through a single pointer. For example:

    FILE *fopen(const char * filename, const char * mode);
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->

The name of the file and its open mode are accessed through unique pointers in fopen(). Therefore, it's possible to preload the values to which the pointers are bound. Indeed, the C99 standard revised the prototype of the function fopen() to the following:

/* new declaration of fopen() in <stdio.h>; */
    FILE *fopen(const char * restrict filename,
                          const char * restrict mode);

Similar changes were applied to the entire standard C library: printf(), strcpy() and many other functions now take restrict pointers:

int printf(const char * restrict format, ...);
    char *strcpy(char * restrict s1, const char * restrict s2);


C++ doesn't support restrict yet. However, since many C++ compilers are also C compilers, it's likely that this feature will be added to most C++ compilers too.

=====================8<================================
上面的例子简单来说就是被const修饰的指针指向的内容真的是不变的吗?在某些情况下显然不是,如第一段代码

void f (const int* pci, int *pi ) // is *pci immutable?
    {
      (*pi)+=1; // not necessarily: n is incremented by 1
       *pi = (*pci) + 2; // n is incremented by 2
    }
    int n;
    f( &n, &n);

pci和pi指向的都是n,虽然pci被const修饰,但pci指向的内容却在函数中被改变了,当用restrict修饰符之后 void f ( const int * restrict pci , int * restrict pi ),问题解决了:一旦我们再有如:f ( &n , &n ) 的调用,编译器将给出错误提示,这是由于一个地址可以通过两个指针来访问。

restrict同样可以用于数组的声明:

If you specify type qualifiers such as void foo(int * restrict a);, the C compiler expresses it with array syntax void foo(int a[restrict]); which is essentially the same as declaring a restricted pointer.

写了这么多,总结一下,其实restrict同const或valiate一样是一个修饰符而已,告诉编译器被restrict修饰的指针所指向的对象,只能通过这个指针或基于这个指针的其他指针进行操作,即限制访问用restrict限制的指针指向的对象只能通过这个指针访问,这对编译器的优化很有好处。

但要注意:restrictC99中新增的关键字,在C89C++中都不支持,在gcc中可以通过—std=c99来得到对它的支持。

posted on 2012-07-24 16:18  #蓝天  阅读(203)  评论(0编辑  收藏  举报

导航