这哥三之间的关系是有趣的,不妨看看这个:
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语义(请见下面的内存顺序说明)。
4. 一个值得注意的例外是 Visual Studio ,其中默认设置下,每个 volatile 写拥有释放语义,而每个 volatile 读拥有获得语义( MSDN ),故而可将 volatile 对象用于线程间同步。标准的 volatile 语义不可应用于多线程编程,尽管它们在应用到 sig_atomic_t 对象时,足以与例如运行于同一线程的 std::signal 处理函数交流。请见: std::memory_order - cppreference.com 末尾说明。
特别注意的误区
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 <stdio.h>
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 <stdio.h>
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 <stdio.h>
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 <stdio.h>
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博客
linux系统的gdb调试c++和反汇编_含影的博客-CSDN博客文章来源:https://www.toymoban.com/news/detail-638256.html
如果使用GDB在Linux调试,GDB下载地址为: Index of /gnu/gdb文章来源地址https://www.toymoban.com/news/detail-638256.html
到了这里,关于关于c++中mutable、const、volatile这三个关键字及对应c++与汇编示例源码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!