C++:const和volatile

在C语言中const类型的数据严格意义上可以修改:

const int a=1;
int*b=&a;
*b=2;

不同于C语言,C++中指针类型是要严格对应的,对const类型的数据必须使用const类型的指针进行接收,从而避免修改;
但问题是c++中同样支持指针的强制类型转换那么问题就出现了,对于下面这段代码输出就会显得不正常:

const int a=1;
int*b=(int*)&a;
*b=2;
cout<<a<<" "<<b<<endl;
cout<<&a<<" "<<b;

这个问题其实是编译器的一种优化,编译器会将需要访问的局部const变量在编译阶段存储在寄存器中(存不下的话不优化),我们再对它的地址内数据进行修改时会显得失效,因为使用的是寄存器和地址上的数据已经无关了。
可以查看下列代码的汇编和运行结果证明。

const int t1=1;
struct A{
    const int t2=1;
};
void fun(const int&p){
	int*q=(int*)&p;
	*q=0;
}
void fun(volatile const int&p){
	int*q=(int*)&p;
	*q=0;
}
int main(){
	const int t3=1;
	A a;
	//fun(t1);报错t1存储在data区只可访问不可修改
	fun(a.t2);
	fun(t3);
	fun(t4);
	volatile const int t4=1;
	cout<<t1<<a.t2<<t3<<t4;
}
fun(int const&):
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-24], rdi
        mov     rax, QWORD PTR [rbp-24]
        mov     QWORD PTR [rbp-8], rax
        mov     rax, QWORD PTR [rbp-8]
        mov     DWORD PTR [rax], 0
        nop
        pop     rbp
        ret
fun(int const volatile&):
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-24], rdi
        mov     rax, QWORD PTR [rbp-24]
        mov     QWORD PTR [rbp-8], rax
        mov     rax, QWORD PTR [rbp-8]
        mov     DWORD PTR [rax], 0
        nop
        pop     rbp
        ret
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 1
        mov     DWORD PTR [rbp-8], 1
        mov     DWORD PTR [rbp-12], 1
        lea     rax, [rbp-12]
        mov     rdi, rax
        call    fun(int const&)
        lea     rax, [rbp-4]
        mov     rdi, rax
        call    fun(int const&)
        lea     rax, [rbp-8]
        mov     rdi, rax
        call    fun(int const volatile&)
        mov     esi, 1
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     rdx, rax
        mov     eax, DWORD PTR [rbp-12]
        mov     esi, eax
        mov     rdi, rdx
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     esi, 1
        mov     rdi, rax
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     rdx, rax
        mov     eax, DWORD PTR [rbp-8]
        mov     esi, eax
        mov     rdi, rdx
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     eax, 0
        leave
        ret

可以看到cout输出未被volatile修饰的const局部变量时都有:

mov     esi, 1

它的作用就是为寄存器赋值,然后接下来对变量的使用会由寄存器代替,volatile会阻止编译期间的优化因而访问时还需访问内存内数据。

volatile的作用是告诉编译器任何时候不对它修饰的成员进行任何优化,打个比方,多线程中一个线程访问完了一个资源,另一个线程也进行了访问,此时缓存如果没有失效第二个读的就是第一个线程的缓存,这样显然是不合理的,缓存失效期间这个资源的状态是未知的,所以可以使用volatile让资源进不去缓存。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值