当前位置:  编程技术>c/c++/嵌入式

C语言中const,volatile,restrict的用法总结

    来源: 互联网  发布时间:2014-10-22

    本文导语:  1. const 变量声明中带有关键词const,意味着不能通过赋值,增量或减量来修改该变量的值,这是显而易见的一点。指针使用const则要稍微复杂点,因为不得不把让指针本身成为const和指针指向的值成为const区别开来、下面的声明...

1. const

变量声明中带有关键词const,意味着不能通过赋值,增量或减量来修改该变量的值,这是显而易见的一点。指针使用const则要稍微复杂点,因为不得不把让指针本身成为const和指针指向的值成为const区别开来、下面的声明表示pf指向的值必须是不变的

constfloat *pf;而pf则是可变的,它可以指向另外一个const或非const值;相反,下面的声明说明pf是不能改变的,而pf所指向的值则是可以改变的:

float* const pf;

最后,当然可以有既不能改变指针的值也不能改变指针指向的值的值的声明方式:

constfloat * const pf;

需要注意的是,还有第三种放置const关键字的方法:

float const * pf; //等价于constfloat * pf;

总结就是:一个位于*左边任意位置的const使得数据成为常量,而一个位于*右边的const使得指针本身成为const

还要注意的一点是关于const在全局数据中的使用:

使用全局变量被认为是一个冒险的方法,它使得数据在程序的任何部分都可以被错误地修改,如果数据是const,那么这种担心就是多余的了不是嘛?因此对全局数据使用const是合理的。

然而,在文件之间共享const数据要格外小心,有两个策略可以使用。一个是遵循外部变量的惯用规则,在一个文件进行定义声明,在其他文件进行引用声明(使用关键字extern)。

/*file1.c------定义一些全局常量*/

const double PI = 3.14159;

/*file2.c-----是用在其他文件中定义的全局变量*/

extern const dounle PI;

另外一个方法是把全局变量放在一个include文件里,这时候需要格外注意的是必须使用静态外部存储类

/*constant.h----定义一些全局常量*/

static const double PI = 3.14159;

/*file1.c-----使用其他文件定义的全局变量*/

#include”constant.h”。

/*file2.c-----使用其他文件定义的全局变量*/

#include”constant.h”

如果不使用关键字static,在文件file1.c和file2.c中包含constant.h将导致每个文件都有同一标识符的定义声明ANSI标准不支持这样做(有些编译器确实支持)。通过使用static, 实际上给了每个文件一个独立的数据拷贝,如果文件想使用该数据与另外一个文件通话,这样做就不行了,因为每个文件只能看见他自己的拷贝,然而由于数据是不 可变的,这就不是问题了。使用头文件的好处是不必惦记在一个文件中进行定义声明,在另一个文件中进行引用声明,缺点在于复制了数据,如果常量很大的话,这 就是个问题了。

2. volatile

限定词volatile告诉编译器,该变量除了可被程序改变意外还可以被其他代理改变。典型的它用于硬件地址和其他并行运行的程序共享的数据。例如,一个地址中可能保存着当前的时钟信息。不管程序做些什么,该地址会随时间改变。另一种情况是一个地址用来接收来自其他计算机的信息;

语法同const:

volatile int a;//a是一个易变的位置

volatile int * pf;//pf指向一个易变的位置

把volatile作为一个关键字的原因是它可以方便编译器优化。

假如有如下代码:

va= x;

//一些不使用x的代码

vb= x;

一个聪明的编译器可能注意到你两次使用了x,但是没有改变它的值,它将把x临时存贮在一个寄存器中,接着,当vb主要x是的时候,它从寄存器而非初始的内存位置得到x的值来节省时间。这个过程被称为缓存。通常缓存是一个好的优化方式,但是如果两个语句中间的其他代理改变了x的值的话就不是这样了。如果没有规定volatile关键字,编译器将无从得知这种改变是否可能发生,因此,为了安全起见,编译器不使用缓存。那是在ANSI以前的情形,现在,如果在声明中没有使用volatile关键字,编译器就可以假定一个值在使用过程中没有修改,它就可以试着优化代码。总而言之,volatile使得每次读取数据都是直接在内存读取而不是缓存。

你可能会觉得奇怪,const和volatile可以同时使用,但是确实可以。例如硬件时钟一般不能由程序改变,这使得他成为const,但他被程序以外的代理改变,这使得他成为volatile,所以你可以同时使用它们,顺序是不重要的:

const volatile time;

volatile表明某个变量的值可能在外部被改变,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。它可以适用于基础类 型如:int,char,long......也适用于C的结构和C++的类。当对结构或者类对象使用volatile修饰的时候,结构或者类的所有成员 都会被视为volatile.

该关键字在多线程环境下经常使用,因为在编写多线程的程序时,同一个变量可能被多个线程修改,而程序通过该变量同步各个线程。

简单示例:

代码如下:

DWORD __stdcall threadFunc(LPVOID signal)
{
int* intSignal=reinterdivt_cast(signal);
*intSignal=2;
while(*intSignal!=1)
sleep(1000);
return 0;
}

该线程启动时将intSignal 置为2,然后循环等待直到intSignal 为1 时退出。显然intSignal的值必须在外部被改变,否则该线程不会退出。但是实际运行的时候该线程却不会退出,即使在外部将它的值改为1,看一下对应的伪汇编代码就明白了:

mov ax,signal
label:
if(ax!=1)
goto label

对于C编译器来说,它并不知道这个值会被其他线程修改。自然就把它cache在寄存器里面。C 编译器是没有线程概念的,这时候就需要用到volatile。volatile 的本意是指:这个值可能会在当前线程外部被改变。也就是说,我们要在threadFunc中的intSignal前面加上volatile关键字,这时 候,编译器知道该变量的值会在外部改变,因此每次访问该变量时会重新读取,所作的循环变为如下面伪码所示:
label:
mov ax,signal
if(ax!=1)
goto label

注意:一个参数既可以是const同时是volatile,是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

3.restrict

关键字restrict通过允许编译器优化某几种代码增强了计算支持。记住,它只能用于指针,并且表明指针是访问一个数据对象的唯一且初始的方式。为了清楚为何这样做,我们需要看一些例子:

代码如下:

intar[10];

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

int* par = ar;


这里,指针restar是访问malloc分配的内存的唯一而且初始的方式,因此声明为restrict。然而,par指针既不是初始的,也不是访问数组ar中数据的唯一方式,所以不用restrict限定词。现在考虑下面这个更加复杂的例子,其中n是一个int
代码如下:

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

{

par[n]+= 5;

restar[n]+= 5;

ar[n]*= 2;

par[n]+= 3;

restar[n]+= 3;

}


知道了restar是访问它所指向的数据的唯一初始方式,编译器就可以用具有同样效果的一条语句来替代包含restar的两个语句

restar[n]+= 8;/*可以替换*/

然而将两个计算par的语句精简为一个则会导致错误因为在par两次访问数据之间,ar改变了该数据的值。没有关键字restrict,编译器将不得不设想比较糟糕的那一种形式,而使用之后,编译器可以放心大胆的寻找计算的捷径。可以将关键字作为指针型函数参量的限定词使用,这意味着编译器可以假定在函数体内没有其他标志符修改指针指向的数据,因而可以试着优化代码,反之不然。来看一下C99标准下C库中的两个函数,他们从一个位置把字节复制到另一个位置

void*memcpy(void* restrict s1,const void* restrict s2,size_t n);

void*memmove(void* s1,const void * s2,size_t);

memcpy要求两个指针的位置不能重叠,但memmove没有这个要求。把s1,s2声明为restrict意味着每个指针都是相应数据的唯一访问方式,因此他们不能访问同一数据块。这满足了不能有重叠的要求。

关键字restrict有两个读者:编译器,它告诉编译器可以自由地做一些优化的假定。另一个读者是用户,他告诉用户仅使用满足restrict要求的参数。一般,编译器没法检查你是否遵循了这一限制,如果你蔑视它,也就是让自己冒险。


    
 
 

您可能感兴趣的文章:

  • Linux下C语言strstr()查找子字符串位置函数详细介绍(strstr原型、实现及用法)
  • 关于C语言中的一些用法,高手进
  • HTML 脚本语言介绍及<script>标签用法
  • C语言变量类型与输出控制用法实例教程
  • linux下进程间通信:共享内存原理及具体用法举例(基于c/c++语言)
  • C语言中char*和char[]用法区别分析
  • c语言中static和extern的用法详细解析
  • C语言中qsort函数用法实例小结
  • C语言入门之指针用法教程
  • 浅析C语言中assert的用法
  • C语言中字符串常用函数strcat与strcpy的用法介绍
  • C语言循环结构与时间函数用法实例教程
  • C语言中system()函数的用法总结
  • 浅析C语言中printf(),sprintf(),scanf(),sscanf()的用法和区别
  • c语言中static的用法详细示例分析
  • C语言、C++中的union用法总结
  • 浅析C语言中sscanf 的用法
  • 基于c语言中调试工具的用法汇总(不包含gdb)
  • 请问:“volatile”是c语言的关键字么?有什么用处?
  • C语言 volatile与const同时使用应注意的问题
  • 探讨C语言中关键字volatile的含义
  • C语言中auto,register,static,const,volatile的区别详细解析
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • C 语言restrict 关键字的使用浅谈
  • 2013年7月和2013年8月编程语言排行榜
  • 如何在GTK2.0下实现国际化(语言选择根据自己设置的语言,不用系统的语言)
  • 2017 年热门编程语言排行榜出炉,你的语言上榜没?
  • C语言中有指针,因此C语言可以创建链表,那么Java语言没有指针,那Java是否可以创建链表呢?
  • 苹果OS X和IOS下最新编程语言swift介绍
  • 求助,在linux下,c语言和汇编语言的接口是什么?
  • c语言判断某一年是否为闰年的各种实现程序代码
  • C语言中间语言 CIL
  • PHP编程语言介绍及安装测试方法
  • 最近学JSP,苦于HTML语言和JAVA语言太差,请教推荐几本书,thanks.
  • c语言实现MD5算法完整代码示例
  • 动态编程语言 LIME编程语言
  • 以NetBeans IDE为例介绍如何使用XML中Schema语言
  • C语言如何改变当前语言环境
  • c语言基于libpcap实现一个抓包程序过程
  • 如何在VIM中使汇编语言和C语言自动缩进?
  • HTML超文本标记语言教程及实例
  • 我安装的linux时默认语言选择的是中文,又乱码,怎么可以解决?怎么更改默认语言成英文?
  • MD5算法的C语言实现
  • Redhat9安装时语言只选择了中文,现在还能再增加其它语言的支持吗?如英文
  • HTML 超文本标记语言简介
  • 请问哪里有ubuntu 9.0版本的中文语言包和KDE的中文语言包下载,我用Google搜索了很多地方都没有!


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3