关于c++中mutable、const、volatile这三个关键字及对应c++与汇编示例源码

这哥三之间的关系是有趣的,不妨看看这个:

cv (const and volatile) type qualifiers - cppreference.com

mutable

permits modification of the class member declared mutable even if the containing object is declared const.

即便一个对象是const的,它内部的成员变量如果被mutable修饰,则此成员变量依旧可以被修改。

const

很常见,用途如其字面意义:别改变它"作用域"内的对象。

volatile

就如其含义:生性活泼,莫对其"作用域"下的对象指手画脚。这里着重说一下volatile。

volatile相关特性

        1. 易变性。所谓的易变性,在汇编层面反映出来,就是两条语句,下一条语句不会直接使用上一条语句对应的volatile变量的寄存器内容,而是重新从内存中读取。
特性。

        2. "不可优化"特性。volatile告诉编译器,不要对我这个变量进行各种激进的优化,甚至将变量直接消除,保证程序员写在代码中的指令,一定会被执行。
        3. "顺序性",能够保证volatile变量间的顺序性,编译器不会进行乱序优化。 C/C++ Volatile变量,与非Volatile变量之间的操作,是可能被编译器交换顺序的。C/C++ Volatile变量间的操作,是不会被编译器交换顺序的。哪怕将所有的变量全部都声明为volatile,哪怕杜绝了编译器的乱序优化,但是针对生成的汇编代码,CPU有可能仍旧会乱序执行指令,导致程序依赖的逻辑出错,volatile对此无能为力 针对这个多线程的应用,真正正确的做法,是构建一个happens-before语义(请见下面的内存顺序说明)。

特别注意的误区

volatile和多线程这种并行数据同步机制无关,也不能解决这类问题。相关问题请见:

内存顺序 std::memory_order - cppreference.com

const 和 volatile 关键字可更改处理指针的方式。 const 关键字指定指针在初始化后无法修改;此后指针将受到保护,防止进行修改。
volatile 关键字指定与后跟的名称关联的值可由用户应用程序中的操作以外的操作修改。 因此,volatile 关键字对于声明共享内存中可由多个进程访问的对象或用于与中断服务例程通信的全局数据区域很有用。
如果某个名称被声明为 volatile,则每当程序访问该名称时,编译器都会重新加载内存中的值。 这将显著减少可能的优化。 但是,当对象的状态可能意外更改时,这是保证可预见的程序性能的唯一方法。

如果将 struct 成员标记为 volatile,则 volatile 将传播到整个结构。 如果结构不具有可通过使用一个指令在当前体系结构上复制的长度,则此结构上可能完全丢失 volatile。

volatile 关键字失效

如果满足下列条件之一,则 volatile 关键字可能对字段不起作用:
        1. 可变字段的长度超过可使用一条指令在当前体系结构上复制的最大大小。
        2. 最外层包含 struct 的长度 - 或如果它是可能嵌套的 struct 的成员 - 超过可使用一条指令在当前体系结构上复制的最大大小。
尽管处理器不会对不可缓存的内存访问重新排序,但必须将不可缓存的变量标记为 volatile,从而保证此编译器不会对内存访问重新排序。
声明为 volatile 的对象不在某些优化中使用,因为它们的值可以随时更改。 系统在请求易失对象时始终读取该对象的当前值,即使前面的指令要求从同一对象获取值也是如此。 此外,对象的值会立即在赋值时写入。

volatile 原理源码示例

注:以下所有示例的c++源码一致,只区分是否使用volatile关键字

c++源程序:

#include 
int main(int argc, char** argv)
{
    auto sk = argc;
    int a = 11 << sk;
    int b = 19 + argc;
    int rv = a + b * 8;
    rv *= sk;
    return rv;
}

VS Debug环境MASM反汇编

没有使用volatile关键字修饰

反汇编代码:

00007FF7DE032993  mov         eax,dword ptr [argc]  
00007FF7DE032999  mov         dword ptr [sk],eax
00007FF7DE03299C  mov         eax,dword ptr [sk]  
00007FF7DE03299F  mov         ecx,0Bh  
00007FF7DE0329A4  mov         dword ptr [rbp+134h],ecx  
00007FF7DE0329AA  movzx       ecx,al  
00007FF7DE0329AD  mov         eax,dword ptr [rbp+134h]  
00007FF7DE0329B3  shl         eax,cl  
00007FF7DE0329B5  mov         dword ptr [a],eax
00007FF7DE0329B8  mov         eax,dword ptr [argc]  
00007FF7DE0329BE  add         eax,13h  
00007FF7DE0329C1  mov         dword ptr [b],eax
00007FF7DE0329C4  mov         eax,dword ptr [a]  
00007FF7DE0329C7  mov         ecx,dword ptr [b]  
00007FF7DE0329CA  lea         eax,[rax+rcx*8]  
00007FF7DE0329CD  mov         dword ptr [rv],eax
00007FF7DE0329D0  mov         eax,dword ptr [rv]  
00007FF7DE0329D3  imul        eax,dword ptr [sk]  
00007FF7DE0329D7  mov         dword ptr [rv],eax
00007FF7DE0329DA  mov         eax,dword ptr [rv]  
00007FF7DE0329DD  lea         rsp,[rbp+148h]  
00007FF7DE0329E4  pop         rdi  
00007FF7DE0329E5  pop         rbp  
00007FF7DE0329E6  ret

使用volatile关键字修饰

c++代码:

#include 
int main(int argc, char** argv)
{
    auto sk = argc;
    int a = 11 << sk;
    int b = 19 + argc;
    volatile int rv = a + b * 8;
    rv *= sk;
    return rv;
}

反汇编代码:

00007FF7F4102993  mov         eax,dword ptr [argc]  
00007FF7F4102999  mov         dword ptr [sk],eax
00007FF7F410299C  mov         eax,dword ptr [sk]  
00007FF7F410299F  mov         ecx,0Bh  
00007FF7F41029A4  mov         dword ptr [rbp+134h],ecx  
00007FF7F41029AA  movzx       ecx,al  
00007FF7F41029AD  mov         eax,dword ptr [rbp+134h]  
00007FF7F41029B3  shl         eax,cl  
00007FF7F41029B5  mov         dword ptr [a],eax
00007FF7F41029B8  mov         eax,dword ptr [argc]  
00007FF7F41029BE  add         eax,13h  
00007FF7F41029C1  mov         dword ptr [b],eax
00007FF7F41029C4  mov         eax,dword ptr [a]  
00007FF7F41029C7  mov         ecx,dword ptr [b]  
00007FF7F41029CA  lea         eax,[rax+rcx*8]  
00007FF7F41029CD  mov         dword ptr [rv],eax
00007FF7F41029D0  mov         eax,dword ptr [rv]  
00007FF7F41029D3  imul        eax,dword ptr [sk]  
00007FF7F41029D7  mov         dword ptr [rv],eax
00007FF7F41029DA  mov         eax,dword ptr [rv]
00007FF7F41029DD  lea         rsp,[rbp+148h]  
00007FF7F41029E4  pop         rdi  
00007FF7F41029E5  pop         rbp  
00007FF7F41029E6  ret

VS Release环境MASM反汇编

没有使用volatile关键字修饰

反汇编代码:

00007FF748801000  mov         eax,0Bh  
00007FF748801005  shl         eax,cl
00007FF748801007  lea         eax,[rax+rcx*8]  
00007FF74880100A  add         eax,98h
00007FF74880100F  imul        eax,ecx
00007FF748801012  ret

使用volatile关键字修饰

反汇编代码:

00007FF650C11005  shl         eax,cl
00007FF650C11007  lea         eax,[rax+rcx*8]  
00007FF650C1100A  add         eax,98h  
00007FF650C1100F  mov         dword ptr [rsp+8],eax
00007FF650C11013  mov         eax,dword ptr [rv]  
00007FF650C11017  imul        eax,ecx  
00007FF650C1101A  mov         dword ptr [rv],eax
00007FF650C1101E  mov         eax,dword ptr [rv]
00007FF650C11022  ret 

Linux GUN x86 64bit AT&T 环境优化参数O1反汇编

没有使用volatile关键字修饰

c++源码:

#include 
int main(int argc, char** argv)
{
    auto sk = argc;
    int a = 11 << sk;
    int b = 19 + argc;
    int rv = a + b * 8;
    rv *= sk;
    return rv;
}

反汇编代码:

0000 89F9     		movl	%edi, %ecx
0002 B80B0000 		movl	$11, %eax
0007 D3E0     		sall	%cl, %eax
0009 8D84F898 		leal	152(%rax,%rdi,8), %eax
0010 0FAFC7   		imull	%edi, %eax
0013 C3       		ret

使用volatile关键字修饰

c++源码:

#include 
int main(int argc, char** argv)
{
    auto sk = argc;
    int a = 11 << sk;
    int b = 19 + argc;
    volatile int rv = a + b * 8;
    rv *= sk;
    return rv;
}

反汇编代码:

0000 89F9     		movl	%edi, %ecx
0002 B80B0000 		movl	$11, %eax
0007 D3E0     		sall	%cl, %eax
0009 8D84F898 		leal	152(%rax,%rdi,8), %eax
0010 894424FC 		movl	%eax, -4(%rsp)
0014 8B4424FC 		movl	-4(%rsp), %eax
0018 0FAFC7   		imull	%edi, %eax
001b 894424FC 		movl	%eax, -4(%rsp)
001f 8B4424FC 		movl	-4(%rsp), %eax
0023 C3       		ret

总结:

        由上面的这些代码可以看到,非Debug环境有优化,但是因为volatile关键字的作用,有没有volatile关键字,优化后的汇编指令是不一样的。

关于Linux下AT&T格式汇编的详情请见: Linux c++反汇编源码细节解释说明_含影的博客-CSDN博客

如果使用GDB在Linux调试,GDB下载地址为: Index of /gnu/gdb

你可能感兴趣的:(c++/c/asm,Assembly,c++,开发语言)