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  */
    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:

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关键字,那么这里编译时会报错,因为一 //个地址可以通过两个指针访问了

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 ; */     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限制的指针指向的对象只能通过这个指针访问,这对编译器的优化很有好处。

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏C/C++基础

C++引用计数(reference counting)技术简介(3)

要想将引用计数施加到现有的实值对象Widget上,按照前面讨论的,都需要修改Winget类的源代码。但是,有时程序库的内容不是我们呢可以修改的,又该如何做呢?

7910
来自专栏每日一篇技术文章

Swift3.0 - 流控制

需求二: 输入一个顶点 判断是否在X轴上,或者Y轴上,或者既不在x轴,也不再Y轴上

8520
来自专栏一个会写诗的程序员的博客

JavaScript 的 async/await : async 和 await 在干什么

async 是“异步”的简写,而 await 可以认为是 async wait 的简写。 async 用于申明一个 function 是异步的,而 await...

7820
来自专栏Kiba518

C#语法——委托,架构的血液

微软用delegate关键字来声明委托,delegate与int,string,double等关键字一样。都是声明用的。

7430
来自专栏一枝花算不算浪漫

[读书笔记]C#学习笔记三: C#类型详解..

364140
来自专栏finleyMa

关于JS中的循环

for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、后文的 ...

17720
来自专栏听雨堂

Python基础语法学习整理

1、基础 r’  ‘:原始字符串 pow、round是内建函数 2、序列通用操作: 索引:d[] 分片:[:] 相加:d+[] 乘法:[1,2]*3...

22760
来自专栏从零开始学 Web 前端

从零开始学 Web 之 ES6(六)ES6基础语法四

在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识点,期间也会分享一些好玩的项目。现在就让我们一起进入 Web 前端学习的冒险之旅吧!

9840
来自专栏xcywt

《Linux命令行与shell脚本编程大全》第二十章 正则表达式

20.1 什么是正则表达式 20.1.1 定义 正则表达式是你所定义的模式模板。linux工具可以用它来过滤文本。 正则表达式利用通配符来描述数据流中第一个或多...

30060
来自专栏Java架构师历程

精选30道Java笔试题解答

都是一些非常非常基础的题,是我最近参加各大IT公司笔试后靠记忆记下来的,经过整理献给与我一样参加各大IT校园招聘的同学们,纯考Java基础功底,老手们就不用进来...

35840

扫码关注云+社区

领取腾讯云代金券