【一天一个C++小知识】002.C++中const和volatile关键字

1. const关键字

  【1】const限制一个变量不能修改其内容,如果强行修改的话,如下面代码这样子,编译就会报错,“表达式必须是可修改的左值”。

const int a = 6;
a = 10;

   【2】如果按如下方式通过指针强行修改,编译可以通过,但是std::cout的话,a的值还是6,但是*p是12,这是因为,编译器对于const类型的变量不会二次读取,只会在最开始的时候从内存中读取一次,之后存储在常量表,之后需要用到就从常量表中取得。

const int a = 6;
int* p = (int*)&a;
*p = 12;

   【3】常量指针:指向常量的指针,也就是指针指向的是一个常量,值不可以改变,但是指针本身地址可以改变,可以指向其他地址,下面两种定义方式都对:

const int* p1;
int const* p2;

   指针常量:指针地址是个常量,指针本身地址不能改变,但是指针指向的变量的值可以改变,只有一种写法如下:

int a = 2;
int* const p3 = &a;

   指针常量是int类型的指针,定义的时候必须初始化地址。

   【4】 const用于聚合,表示“不能改变的一块地址”,但是不能再编译期间使用它的值,因为编译器在编译期间不需要知道存储的内容,也就是下面的代码中有两句是非法的:

const int i[]={ 1, 2, 3, 4 };
float f[i[3]];//Illegal
struct S { int i, j;};
const S s[]=d[s[1].j];//Illegal

  【5】C语言默认const为外部链接,所以代码const int bufsize=100;char buf[bufsize]是错误的,因为bufsize占用某块内存,而编译器不知道它在编译时的值。C中可以这样写const int bufsize;,但这在C++中是错误的,因为C++默认const为内部链接,必须在定义时初始化。

  【6】C++中定义const指定为外部链接有两种方式:1)extern const int x=1;,这样初始化并指定为extern,强迫编译器给x分配内存(事实上编译器这里仍然可能选择常量折叠),初始化使它成为一个定义而非声明。2)extern const int x;,意味着在别处进行了定义(C中这总是一个定义,所以不一定在别处进行了定义),C++中使用extern const却不初始化,那么编译器就不能使用常量折叠而必须分配内存。

  【6】const代替宏定义以消除宏的不安全性:

#define A1 100.5
const int A2=100.5;
char P1[A1];//Illegal
char P2[A2];//OK

【7】const必须在建立它的时候被初始化,但是在类中使用一个非static的const时(static const必须在建立时初始化),不能给他初值,初始化必须在构造函数中进行。这两件事情的矛盾可以由构造函数初始化列表解决,如下:

class A{
	const int a;
public:
	A(int aa);
	void print();
};
A::A(int aa):a(aa){}
void A::print(){cont<<aa<<endl;} 
int main(){
	A a(1),b(2);
	a.print();
	b.print();
}

2. volatile关键字

  这个关键字的作用在于告诉编译器这个变量可变,不要对其进行优化。因为通常来说,编译器会对代码进行优化。比如下面的代码:

volatile int i = 10;
int a = i;
...// 其他代码,并未明确告诉编译器,对 i 进行过操作
int b = i;

  volatile 指出 i 是随时可能发生变化的,每次使用它的时候必须从 i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在 b 中。而优化做法是,由于编译器发现两次从 i读数据的代码之间的代码没有对 i 进行过操作,它会自动把上次读的数据放在 b 中。而不是重新从 i 里面读。这样以来,如果 i是一个寄存器变量或者表示一个端口数据就容易出错,所以说 volatile 可以保证对特殊地址的稳定访问。但是,在 VC 6 中,一般调试模式没有进行代码优化,所以这个关键字的作用看不出来。如下代码:

int i = 10;
int a = i;
printf("i = %d", a);
// 下面汇编语句的作用就是改变内存中 i 的值但是又不让编译器知道
__asm {
	mov dword ptr[ebp - 4], 20h
}
int b = i;
printf("i = %d", b);

  debug模式输出10和32,但是release模式输入10和10。其可以确保每次使用变量的时候,都从内存中重新读取,而不允许编译器对这个变量的读取操作进行优化。一般在多任务环境下会用到这个关键字。

  与const类似,有:

volatile int* p1;//volatile int类型的指针,禁止编译器优化指针指向的变量,但是可以优化指针本身地址
int volatile* p2;//同上,另一种写法

int* volatile p3;//int类型的指针,禁止编译器优化指针本身地址,但是可以优化指针指向的变量

  多线程环境下有些变量是用volatile关键字声明的。当两个线程都要用到某一个变量且该变量的值会被改变时,应用volatile声明,该关键字的作用是防止优化编译器把变量从内存装入CPU寄存器中。如果变量被装入寄存器,那么两个线程有可能一个使用内存中的变量,一个使用寄存器中的变量,这会造成程序的错误执行。volatile的意思是让编译器每次操作该变量时一定要从内存中真正取出,而不是使用已经存在寄存器中的值,如下:

volatile  BOOL  bStop = FALSE;
//(1) 在一个线程中:
while (!bStop) { ... }
bStop = FALSE;
return;
//(2) 在另外一个线程中,要终止上面的线程循环:
bStop = TRUE;
while (bStop);
//等待上面的线程终止,如果bStop不使用volatile申明,那么这个循环将是一个死循环,因为bStop已经读取到了寄存器中,
//寄存器中bStop的值永远不会变成FALSE,加上volatile,程序在执行时,每次均从内存中读出bStop的值,就不会死循环了。

欢迎扫描二维码关注微信公众号 深度学习与数学   [每天获取免费的大数据、AI等相关的学习资源、经典和最新的深度学习相关的论文研读,算法和其他互联网技能的学习,概率论、线性代数等高等数学知识的回顾]
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值