C语言也是有所发展的,restrict 关键字有什么用?

正如我前面的文章中提到的,C语言虽然已经比较成熟,但是近些年来也是有所发展的——增加了一些新特性。遗憾的是,可能因为C语言程序员的工资比不过互联网程序员,国内很多教材比较老旧,几乎不涉及近些年来C语言新增的新特性。

事实上,目前大多数平台已经至少支持 C99 标准,而 C99 中新增的一些特性或者关键字的确非常好用,能够大大提升C语言程序的开发效率,以及增加代码可读性。

restrict 关键字

例如,C99 中新增了 restrict 关键字,它能够帮助编译器优化C语言代码,生成效率更高的程序。

确切来说,restrict 是为C语言指针服务的关键字,程序员可以通过 restrict 通知编译器指针索引内存的一些特征,便于编译器有选择的优化代码,生成高效程序。

restrict 关键字修饰指针时,用于通知编译器只有该指针以及该指针的派生值(例如指针+1)才用于访问指向的对象,别的指针不会和该指针一样指向相同的对象。这有助于编译器执行优化,稍后读者可通过一个例子看到这一点。

如果编译器不能确定上面这样的特征,将不敢执行优化,否则可能导致程序出现“未定义”的行为。原则上来说,合理的使用 restrict 关键字,可以让最终的C语言程序提升相当多的性能。

实例

接下里,本文将以两个简单实例讨论 restrict 关键字的作用,请看下面这段C语言代码:

void updatePtrs(size_t *ptrA, size_t *ptrB, size_t *val)
{
  *ptrA += *val;
  *ptrB += *val;
}

代码很简单,无非就是将前面 ptrA 和 ptrB 指向的值加上 val 指向的值。但是由于 ptrA,ptrB,val 三个指针可能指向同一块内存,所以编译器不能最大程度执行优化,因此最终生成的指令可能是下面这样的:

load R1 ← *val  ;从地址 val 处加载值到寄存器 R1 里
load R2 ← *ptrA
add R2 += R1    ;执行加操作,结果放在寄存器 R2 里
store R2 → *ptrA    ;将寄存器 R2 中的值传递到地址 ptrA 处
load R2 ← *ptrB
load R1 ← *val
add R2 += R1
store R2 → *ptrB

不过,如果使用了 restrict 关键字修饰三个指针,情况就不同了。修改后的 updatePtrs() 函数的C语言原型如下,请看:

void updatePtrs(size_t *restrict ptrA, 
                    size_t *restrict ptrB, 
                    size_t *restrict val);

应注意,程序员使用 restrict 关键字修饰指针时,应自己确保三个指针指向的内存没有指向同一块内存。因为编译器此时就是这么认为的,因此它可以重新安排代码,执行优化,最终生成下面这样的指令:

load R1 ← *val
load R2 ← *ptrA
load R3 ← *ptrB
add R2 += R1
add R3 += R1
store R2 → *ptrA
store R3 → *ptrB

显然,这种情况下,程序只需加载 val 指向的值一次。此外,由于编译器可以更自由地优化C语言代码,因此编译器可以生成执行效率更高的程序。

从上述生成的指令可以看出,存储操作都是在加载操作之后进行的,这确保了处理器不必在代码中间阻塞以等待存储操作完成。

最后

应该注意,在不同的机器上编译C语言程序时,实际生成的指令可能不同。但是 restrict 关键字的作用是相同的——它通知编译器可以更加自由的优化代码,以提升最终C语言程序的效率。

阅读更多:   C语言
添加新评论

icon_redface.gificon_idea.gificon_cool.gif2016kuk.gificon_mrgreen.gif2016shuai.gif2016tp.gif2016db.gif2016ch.gificon_razz.gif2016zj.gificon_sad.gificon_cry.gif2016zhh.gificon_question.gif2016jk.gif2016bs.gificon_lol.gif2016qiao.gificon_surprised.gif2016fendou.gif2016ll.gif