关于restrict的小问题

最近在看C语言核心技术这本书。看到指针。有一些概念不太清楚!

restrict是用来优化的,是C99新加的关键字,估计流行的编译器还不 支持 。    
  你可以把restrict删除,然后直接编译,运行结果一样的,最多慢一点。  
  和const用法差不多,但是意思不一样。    
  C/C++灵活的语法限制了它们的计算速度。譬如这个 函数 :    
  void   func(const   int*   values1,const   int*   values2,int*   values3,int   valueNum)    
  {    
  for(int   i=0;i<valueNum;++i)    
  values3[i]+=value1[i]*values2[i];    
  }    
  如果CPU支持矩阵运算,譬如可以用一条指令来计算两个向量的积,上面这个函数就可以被优化为一条指令。但是,事实上是不可能的。因为这种指令可能是:    
  a.先读入两个向量的所有值到寄存器    
  b.计算,将结果寄存器里。    
  c.写回计算结果。    
  所以,上面这个不能被优化,因为编译器不能保证values1和values2的内容在这个循环里不改变。因为,虽然有const修饰,但是这个const是语法/语义上的,而不一定是真实的。譬如,有个人这样调用这个函数:    
  int   a[10];    
  func(a,a,a,10);    
  这样,如果用那个特殊指令去优化,可能造成实际结果和预计结果不一样。所以,不能优化。    
  但是如果你把前两个指针声明为restrict,就表示,在这个函数内,这两个指针的值得任何改变,都是通过这两个指针进行的。这样,编译器就可以自由优化了。从而使C可以达到Fortran一样的运算效率。 

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:  
  

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来得到对它的支持。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值