volatile、const关键字的理解
一个程序中数据的计算是通过CPU。那么CPU中的数据来自哪里呢?
1. 内存(物理内存):所以一个进程的虚拟内存操作后会通过MMU将虚拟内存中的数据映射到物理内存
2. 寄存器(cpu的寄存器):当一个变量的优化级别较高时,会将变量存到cpu的寄存器中
用信号理解volatile
代码说明:
#include <stdio.h> int flag = 0; void sig_handle(int signo) { printf("signo = %d\n",signo); flag = 1; } int main() { signal(2,sig_handle); while(!flag) ; return 0; }
这个程序想要的效果是主函数阻塞,当按了ctl+c后,程序结束。
但实际上是当按下ctl+c后,回打印printf语句,程序不会结束。
原因:
main函数和和信号处理函数是两个执行流,while循环在main函数中,编译器只会检查main函数,发现flag并没有main函数中改变。在优化级别较高的时候,flag会被直接写进cpu的寄存器中,cpu读取flag时直接从寄存器里读,信号处理函数改变flag改变的是flag在内存中的值,寄存器中flag的值始终保持0。
程序改进:
#include <stdio.h> volatile int flag = 0; void sig_handle(int signo) { printf("signo = %d\n",signo); flag = 1; } int main() { signal(2,sig_handle); while(!flag) ; return 0; }
直接解释volatile:
vilatiele:易变的,说明被volatile修饰的变量在程序运行过程中可能会被改变。所以每次在读值的时候,都要去内存中拿,这样拿到的值才是正确的(防止编译器优化))。
int main() { int a = 1; int b = a; int c = a; return 0; }
计算机的惰性原则,为了计算速度往往不会去执行没用的操作,在优化代码的时候它发现b和c的值都是等于a的,所以在执行int b=a的时候他会到内存中把a的值1取出来赋值给b,这一点没什么毛病,但是下一条语句它发现c也是等于a的,本来按照刚刚的流程进到内存里面把a拿出来再给c就可以了,但是编译器发现执行int b=a的时候已经拿过一次a了,并且a的值还没有改变,那它为了省事它就不到内存去拿,而是把刚刚拿出来在缓存里的a给c,这样就减少了一个读取的操作,提高了效率。 int main() { volatile int a = 1; int b = a; int c = a; return 0; }
声明volatile变量a的时候,就是告诉编译器这个变量可能在运行的时候会被改变(防止优化),所以对a操作的时候都是去内存取值,即b=a,c=a都是从内存中读取a的值,这样就能确保我们拿到的是变化后的值
什么时候使用volatile?
1. 多线程中共享的变量
2. 中断服务子程序中会访问到的非自动变量
3. 并行设备的硬件寄存器(如:状态寄存器)
const关键字:
const:常量变量,用const修饰的变量是只读变量,不能修改
1. 修饰变量:
const int a = 1;//定义并初始化一个常变量a
a = 2;//错误,a为只读,不能修改
const int b;//错误,const修饰的变量在定义的时候必须初始化
2. 修饰函数的参数:
void setmu(const int a)//在这个函数中不能对a进行修改
{
a = 1;错误,不能改变
}
3. 修饰函数:
const修饰函数,说明函数的返回值不能修改,并且该返回值只能用带const的变量接收
const char getstring();
char *str = getstring()//错误,因为str没有const修饰
const char *str = getstring()//这样就正确
4. 修饰指针
a.常量指针:指针指向的那块地址内容不能被改变(既不能用*p去改变地址里的内容),但是这个指针变量本身却可以指向多个地址
理解:const修饰的*p,所以指向的那块地址里的内容不能修改
int const *p;
const int *p;//这两个是一样的
如:int a = 3;
int b = 4;
int const *pa = &a;
*pa = 4;//错误,不能通过*pa改变他指向的那块地址的值
printf(“*pa = %d\n”,*pa);//输出3
a = 4;
printf(“*pa = %d\n”,*pa);//输出4
pa = &b;//可以指向多个地址
b.指针常量:这个指针变量的值不能改变(即只能指向一个确定的地址了),但是指向的那块地址的内容可以改变
理解:修饰的是p,所以p的值不能改变
int *const p;
如:int a = 2;
int b = 1;
int *const pa = &a;
*pa = 6;//对的
pa = &b;//错误,现在是指针常量
int const * p const;
int const * const p;//指针指向的内容和指针本身都不能改变