C语言之volatile、static、const、extern
C语言之volatile、static、const、extern
1.volatile
1.1 volatile基本含义
volatile是一个类型修饰符(type specifier),是被设计用来修饰被不同线程访问和修改的变量;volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接到内存中读值。
volatile防止代码被编译器优化,例如:
a=1;
a=2;
a=3;
编译器会进行优化,优化为只有a=3(即忽略前三条语句,只产生一条机器代码);如果键入volatile,则编译器会逐一地进行编译并产生相应的机器代码(产生四条代码)
volatile声明的变量是时刻都有可能变的,例如:volatile int a;a*a并不一定是a的平方。
1.2 volatile常见用途
volatile变量的几个例子:
1)并行设备的硬件寄存器(如:状态寄存器)
2)一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3)多线程应用中被几个任务共享的变量
1.3 volatile常见提问
1.一个参数既可以是const还可以是volatile吗?
可以的,例如只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2.一个指针可以是volatile 吗?
可以,当一个中服务子程序修该一个指向一个buffer的指针时。
2 static
2.1 static基本含义
static关键字主要用于定义全局静态变量、局部静态变量、静态函数。
2.2 static 全局静态变量
全局静态变量:当一个进程的全局变量被声明为static之后,它的中文名叫静态全局变量。静态全局变量和其他的全局变量的存储地点并没有区别,都是在.data段(已初始化)或者.bss段(未初始化)内,但是它只在定义它的源文件内有效,其他源文件无法访问它。
2.3 static 局部静态变量
普通的局部变量在栈空间上分配,这个局部变量所在的函数被多次调用时,每次调用这个局部变量在栈上的位置都不一定相同。局部变量也可以在堆上动态分配,但是记得使用完这个堆空间后要释放之。
static局部变量中文名叫静态局部变量。它与普通的局部变量比起来有如下几个区别:
1.位置:静态局部变量被编译器放在全局存储区.data(注意:不在.bss段内,原因见3)),所以它虽然是局部的,但是在程序的整个生命周期中存在。
2.访问权限:静态局部变量只能被其作用域内的变量或函数访问。也就是说虽然它会在程序的整个生命周期中存在,由于它是static的,它不能被其他的函数和源文件访问。
3.值:静态局部变量如果没有被用户初始化,则会被编译器自动赋值为0,以后每次调用静态局部变量的时候都用上次调用后的值。这个比较好理解,每次函数调用静态局部变量的时候都修改它然后离开,下次再调用该函数,读的时候从全局存储区读出的静态局部变量就是上次修改后的值。
需要注意的是由于static局部变量的这种特性,使得含静态局部变量的函数变得不可重入,即每次调用可能会产生不同的结果。这在多线程编程时可能会成为一种隐患。需要多加注意。
2.4 static函数
static函数的作用域是本源文件,static函数可以很好地解决不同原文件中函数同名的问题,因为一个源文件对于其他源文件中的static函数是不可见的。
参考:https://zhuge.blog.csdn.net/article/details/76850375
3 const
3.1 const基本含义
const-英文意思-常量,它限定了一个变量时不允许被修改,可以产生静态作用,使用const在一定程度上可以提高程序的安全性和可靠性。
3.2 const 与 #define
预处理指令 #define xxx 可以很方便的进行值替代,这种值替代至少在3个方面有点突出,一是避免意义模糊的数字出现,使得程序语意流畅清晰,二是可以很方便的进行参数的调整与修改,只需要在宏定义那修改,整个程序中所有引用该宏定义的地方都随之更改。三是提高了程序的执行效率,由于是使用“预编译器”进行值替代,并不需要为这些常量存储空间,所以执行效率高。
说道这里,可能会迷惑以上这些内容与const有什么关系呢?
预处理语句虽然有上面说的有点,但是它也有缺点,即预处理语句仅仅只是简单的值替代,缺乏“类型”检测机制,这样预处理语句就不能享受编译系统的类型检查好处了,从而可能成为引发一系列错误的隐患,比如在程序中,定义一个只读变量“u8 name[]=“tom”,在编程中如果对name[1]进行赋值,可能就会引起一系列的错误。
所以,const的推出的初始目的,正是为了取代预编译指令,消除它的缺点,同事继承它的优点。
const作用及优点:
1.以const修饰的常量值,具有不可变性,这是它能取代预定义语句的基础。
2.很明显,它也同样可以避免意义模糊的数字出现,可以很方便的进行参数的调整和修改。
3.C/C++编译器通常不会为const变量分配RAM存储空间,而是将他们保存在符号表中,这就使得它成为了一个编译期间的常量,没有了存储与读内存操作,使得它的效率很高,同时,这也是它取代预定义的重要基础,编译器在编译它时,不会去读存储的内容,而是直接从符号表中替代。这是一种特别特别特别好的优点,比如在存放字库或者图片库时,这些库的对应的数组体积会特别大。
3.3 const常见用途
3.3.1 const常量
由于常量一旦被创建后其值就不能再改变,所以常量必须在定义的同时赋值(初始化),后面的任何赋值行为都将引发错误。
3.3.2 const与指针
const 也可以和指针变量一起使用,这样可以限制指针变量本身,也可以限制指针指向的数据。const 和指针一起使用会有几种不同的顺序,如下所示:
const int *p1;
int const *p2;
int * const p3;
在最后一种情况下,指针是只读的,也就是 p3 本身的值不能被修改;在前面两种情况下,指针所指向的数据是只读的,也就是 p1、p2 本身的值可以修改(指向不同的数据),但它们指向的数据不能被修改。
当然,指针本身和它指向的数据都有可能是只读的,下面的两种写法能够做到这一点:
const int * const p4;
int const * const p5;
const 离变量名近就是用来修饰指针变量的,离变量名远就是用来修饰指针指向的数据,如果近的和远的都有,那么就同时修饰指针变量以及它指向的数据。
3.3.3 const与函数形参
在C语言中,单独定义 const 变量没有明显的优势,完全可以使用#define命令代替。const 通常用在函数形参中,如果形参是一个指针,为了防止在函数内部修改指针指向的数据,就可以用 const 来限制。例如:int strcmp ( const char * str1, const char * str2 );
3.3.4 const 和非 const 类型转换
当一个指针变量 str1 被 const 限制时,并且类似const char *str1这种形式,说明指针指向的数据不能被修改;如果将 str1 赋值给另外一个未被 const 修饰的指针变量 str2,就有可能发生危险。因为通过 str1 不能修改数据,而赋值后通过 str2 能够修改数据了,意义发生了转变,所以编译器不提倡这种行为,会给出错误或警告。
const char *和char *是不同的类型,不能将const char *类型的数据赋值给char *类型的变量。但反过来是可以的,编译器允许将char *类型的数据赋值给const char *类型的变量。
char *指向的数据有读取和写入权限,而const char *指向的数据只有读取权限,降低数据的权限不会带来任何问题,但提升数据的权限就有可能发生危险。
4 extern
4.1 extern含义
extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。
extern有两个作用:
第一个,当它与"C"一起连用时,如: extern “C” void fun(int a, int b);则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的。
第二,当extern不与"C"在一起修饰变量或函数时,如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块活其他模块中使用,记住它是一个声明不是定义。
4.2 extern 和 static
(1) extern 表明该变量在别的地方已经定义过了,在这里要使用那个变量.
(2) static 表示静态的变量,分配内存的时候, 存储在静态区,不存储在栈上面.
static 作用范围是内部连接的关系, 和extern有点相反.它和对象本身是分开存储的,extern也是分开存储的,但是extern可以被其他的对象用extern 引用,而static 不可以,只允许对象本身用它. 具体差别首先,static与extern是一对“水火不容”的家伙,也就是说extern和static不能同时修饰一个变量;其次,static修饰的全局变量声明与定义同时进行,