分享

不常用,却必须了解的几个C语言限定符?volatile,restrict,const

 豫龙晏子ylyz 2019-02-12

卷首语:

Qualifier,有的叫修饰符,有的叫限定符。除了const外,其余都很少用到,但又必须掌握,尤其是对于嵌入式编程,直接操作一些硬件寄存器,若修饰符使用不当,很可能就会遇到一些让自己一头雾水的现象。

不常用,却必须了解的几个C语言限定符?volatile,restrict,const

C语言的四个限定符

C90新增:const 和 volatile

C99新增:restrict

C11新增:_Atomic

备注:

(1)C89、C90、ANSI C通常指的是同一个C语言标准。1989年,美国国家标准协会(ANSI)推出C语言和C标准库的标准。该标准通常被称为ANSI C。由于该标准是1989年推出的,因此也被称为C89。时隔一年,1990国际标准协会ISO参照ANSI标准,推出一模一样的C语言和C标准库标准,由于该标准是1990年提出的,因此被称为C90标准。因此,C89, C90, ANSI C是同一个标准。

(2)可以通过给编译器传递参数来指定所用标准,但一般编译器对C99和C11的支持都是不完整的。如何区别?看下是否编译报错即可。

限定符一:const

最为常用,用于限定变量为constancy(很久不变),即常量。举几个例子,说明其用法和注意事项。

不常用,却必须了解的几个C语言限定符?volatile,restrict,const

const用法说明

const 用法均不难理解,唯独指针比较列外,因为要区分是限定指针本身为const还是限定指针指向的值为const。如上图所述,const放在*左边,限定的是指针指向的值;放在*的右边,限定的是指针本身,不能再指向别的地方。

const int *P1: 在*左边,P1指向一个const int常量,*P1不能被修改,但P1可以被修改int * const P2 : 在*右边,P2是一个const指针, P2不能被修改,但*P2可以被修改const int *const P3:表面P3不能指向别处,其指向的数据,*P3也不能被修改

经典用法(1):

在constant.h中:

static const double PI = 3.1415926;

定义常量的时候,其与地方只能引用式声明,如果给const常量加上一个static修饰符,则可以被所有文件所包含(include),只不过,每个文件都将有一个拷贝,如果是大数组之类,就比较浪费空间。

好处就是不必在别的文件,到处使用引用式声明: extern const double PI;

经典用法(2):

避免多次拷贝,也可以在constant.c中定义:

const double PI = 3.1415926;

然后在constant.h中使用引用式声明,使用者include 'constant.h' 即可:

extern const double PI;

还有更好的用法的同学,可以评论分享。

限定符二:volatile

其语法和const一样。作用是告诉编译器其所修饰的变量,不可被优化

举例1

int *x = p;

a = *x;

/..一段没有使用x的代码.../

b = *x;

如果不适用volatile限定 int x变量,编译器会认为x值在这段代码中是不变的,于是将x临时存到一个空闲的寄存器中,即cache优化,这样使用x的值的时候,直接从寄存器中读取,就能提升效率。

但如果在另外一个线程里,如果发生了 *p=其他值 ,即有其他人能修改了x指向的值,就会发生数据的不一致错误。这个时候就需要使用volatile。

举例2

char *p = &led_address;//如果LED亮度为0~255;

*p=100;

/* 省略一段与*p无关的代码 */

*p=255;

在这段代码中,本来应该看到LED亮度先是100,后续再变为255(最亮),但是因为没有volatile修饰的关系,某些编译器可能会将*p直接合并为一句,在最后直接令*p=255,这样就看不到LED灯亮度的变化过程。

限定符三:restrict

与volatile相反,restrict告知编译器,其所修饰的变量,可以被优化,只能用于指针,表明所修饰的指针变量,是所指数据的唯一访问者。

同样的,restrict 也可告诉读者,restrict指针所指向的数据,不要且不能再有其他访问者。

 int * restrict rP = (int *) malloc(10 * sizeof(int)); for(int i=0; i<10; i++) { rp[i]+=1; rp[i]+=3; }在这种情况下,编译器得知 rP 是所指内存的唯一访问者,会将 rp[i]+=1; rp[i]+=3;合并为一句: rp[i]+=4;

限定符四:_Atomic

即原子类型。在多线程编程中,对于多个线程均可访问的变量,需要保证其数据的一致性,即当一个线程对一个原子类型的对象执行原子操作时(读或写),其他线程不能访问它,否则可能导致数据的不一致。

C11通过包含可选的头文件stdatomic.h和threads.h,提供了一些可选的(不是必须实现的)管理方法。值得注意的是,要通过各种宏函数来访问原子类型

int DATA;// 普通声明DATA= 12;  // 普通赋值转变为原子类型:_Atomic int DATA; // DATA是一个原子类型的变量atomic_store(&DATA, 12);  // 修改原子类型DATA的数据,函数为stdatomic.h中的宏

卷尾语:

我们通常用类型和存储类别来描述一个变量,本文是下面文章一个补充,完成对C语言变量另一个层面的解读。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多