欢迎来到study-hard-forever的博客

关键字const、volatile与指针的使用;汇编语言与寄存器状态的查看

关键字const、volatile与指针的使用;汇编语言与寄存器状态的查看:

首先来看一个例子,为了防止是编译器自身优化的原因导致结果不一致性我们在这里禁止编译器优化(实际上这里与编译器优化无关):
例一(const与指针的使用导致的结果不一致性):

#include <iostream>
#include<stdio.h>
using namespace std;
int main()
{

    #pragma optimize("", off)  //禁止编译器优化下面一段代码
    const int a = 10;
    printf("%d\n",a);
    int *p = (int*)&a;
    *p = 100;
    printf("%p\n%p\n",&a,p);  //地址一样
    printf("%d,%d\n",a,*p);  //值不一样
    return 0;
    #pragma optimize("", on)
}


result:
在这里插入图片描述
原因:

	int *p = (int*)&a;
    *p = 100;

此处代码确实是将地址部分的内容加以改正了,但是a首先定义为const变量,所以编译器认为a在以后是不会改变的,因此在程序编译的时候,就把变量a开始的值10放在了寄存器中,以后访问的时候直接从寄存器中读取;故执行上述代码片段时虽然修改了内存中实际的值,但是却没有修改寄存器里的值,输出a的值的时候,还是从寄存器的读取的,而寄存器的值依然是10,所以导致了读取的值和实际值不相符。
例二(通过volatile实现结果的一致性):

#include <iostream>
#include<stdio.h>
using namespace std;
int main()
{
    volatile const int a = 10;  //看是否加入了限定字volatile,如果加上则结果值一样,反之则不一样
    //变量如果加了 volatile 修饰,则会从内存重新装载内容,而不是直接从寄存器拷贝内容。 
    //const int a = 10;
    printf("%d\n",a);
    int *p = (int*)&a;
    *p = 100;
    printf("%p\n%p\n",&a,p);  //地址一样
    printf("%d,%d\n",a,*p);  //值不一样
    return 0;
}

加入volatile关键字之后,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据,而且读取的数据立刻被保存。因此使用volatile关键字避免了读取寄存器和读取内存的不一致性错误。
result:
在这里插入图片描述
通过上述两个例子也可以说明const并不是绝对安全的,const关键字并不能保证常量值被修改(可以通过指针直接修改地址值的方式等进行修改)。
当然上述例子我们可以在调试过程中通过观察汇编代码和寄存器状态进一步得出结论,有汇编语言基础的可以自行尝试一下。

例一(汇编代码):

#include <iostream>
#include<stdio.h>
using namespace std;
int main()
{
00007FF63C1C18F0  push        rbp  
00007FF63C1C18F2  push        rdi  
00007FF63C1C18F3  sub         rsp,128h  
00007FF63C1C18FA  lea         rbp,[rsp+20h]  
00007FF63C1C18FF  mov         rdi,rsp  
00007FF63C1C1902  mov         ecx,4Ah  
00007FF63C1C1907  mov         eax,0CCCCCCCCh  
00007FF63C1C190C  rep stos    dword ptr [rdi]  
00007FF63C1C190E  mov         rax,qword ptr [__security_cookie (07FF63C1CC008h)]  
00007FF63C1C1915  xor         rax,rbp  
00007FF63C1C1918  mov         qword ptr [rbp+0F8h],rax  
00007FF63C1C191F  lea         rcx,[__21349430_ConsoleApplication1@cpp (07FF63C1D1026h)]  
00007FF63C1C1926  call        __CheckForDebuggerJustMyCode (07FF63C1C108Ch)  
    //volatile const int a = 10;  //看是否加入了限定字volatile,如果加上则结果值一样,反之则不一样
    const int a = 10;
00007FF63C1C192B  mov         dword ptr [a],0Ah  
    printf("%d\n",a);
00007FF63C1C1932  mov         edx,0Ah  
00007FF63C1C1937  lea         rcx,[string "%d\n" (07FF63C1C9CA4h)]  
00007FF63C1C193E  call        printf (07FF63C1C11E0h)  
    int *p = (int*)&a;
00007FF63C1C1943  lea         rax,[a]  
00007FF63C1C1947  mov         qword ptr [p],rax  
    *p = 100;
00007FF63C1C194B  mov         rax,qword ptr [p]  
00007FF63C1C194F  mov         dword ptr [rax],64h  
    printf("%p\n%p\n",&a,p);  //地址一样
00007FF63C1C1955  mov         r8,qword ptr [p]  
00007FF63C1C1959  lea         rdx,[a]  
00007FF63C1C195D  lea         rcx,[string "%p\n%p\n" (07FF63C1C9CA8h)]  
00007FF63C1C1964  call        printf (07FF63C1C11E0h)  
    printf("%d,%d\n",a,*p);  //值不一样
00007FF63C1C1969  mov         rax,qword ptr [p]  
00007FF63C1C196D  mov         r8d,dword ptr [rax]  
00007FF63C1C1970  mov         edx,0Ah  
00007FF63C1C1975  lea         rcx,[string "%d,%d\n" (07FF63C1C9CB0h)]  
00007FF63C1C197C  call        printf (07FF63C1C11E0h)  
    return 0;
00007FF63C1C1981  xor         eax,eax  
}
00007FF63C1C1983  mov         edi,eax  
00007FF63C1C1985  lea         rcx,[rbp-20h]  
}
00007FF63C1C1989  lea         rdx,[__xt_z+1E0h (07FF63C1C9C80h)]  
00007FF63C1C1990  call        _RTC_CheckStackVars (07FF63C1C1343h)  
00007FF63C1C1995  mov         eax,edi  
00007FF63C1C1997  mov         rcx,qword ptr [rbp+0F8h]  
00007FF63C1C199E  xor         rcx,rbp  
00007FF63C1C19A1  call        __security_check_cookie (07FF63C1C10DCh)  
00007FF63C1C19A6  lea         rsp,[rbp+108h]  
00007FF63C1C19AD  pop         rdi  
00007FF63C1C19AE  pop         rbp  
00007FF63C1C19AF  ret 

例二(汇编代码):

#include <iostream>
#include<stdio.h>
using namespace std;
int main()
{
00007FF7D37E18F0  push        rbp  
00007FF7D37E18F2  push        rdi  
00007FF7D37E18F3  sub         rsp,128h  
00007FF7D37E18FA  lea         rbp,[rsp+20h]  
00007FF7D37E18FF  mov         rdi,rsp  
00007FF7D37E1902  mov         ecx,4Ah  
00007FF7D37E1907  mov         eax,0CCCCCCCCh  
00007FF7D37E190C  rep stos    dword ptr [rdi]  
00007FF7D37E190E  mov         rax,qword ptr [__security_cookie (07FF7D37EC008h)]  
00007FF7D37E1915  xor         rax,rbp  
00007FF7D37E1918  mov         qword ptr [rbp+0F8h],rax  
00007FF7D37E191F  lea         rcx,[__21349430_ConsoleApplication1@cpp (07FF7D37F1026h)]  
00007FF7D37E1926  call        __CheckForDebuggerJustMyCode (07FF7D37E108Ch)  
    volatile const int a = 10;  //看是否加入了限定字volatile,如果加上则结果值一样,反之则不一样
00007FF7D37E192B  mov         dword ptr [a],0Ah  
    printf("%d\n",a);
00007FF7D37E1932  mov         edx,dword ptr [a]  
00007FF7D37E1935  lea         rcx,[string "%d\n" (07FF7D37E9CA4h)]  
00007FF7D37E193C  call        printf (07FF7D37E11E0h)  
    int *p = (int*)&a;
00007FF7D37E1941  lea         rax,[a]  
00007FF7D37E1945  mov         qword ptr [p],rax  
    *p = 100;
00007FF7D37E1949  mov         rax,qword ptr [p]  
00007FF7D37E194D  mov         dword ptr [rax],64h  
    printf("%p\n%p\n",&a,p);  //地址一样
00007FF7D37E1953  mov         r8,qword ptr [p]  
00007FF7D37E1957  lea         rdx,[a]  
00007FF7D37E195B  lea         rcx,[string "%p\n%p\n" (07FF7D37E9CA8h)]  
00007FF7D37E1962  call        printf (07FF7D37E11E0h)  
    printf("%d,%d\n",a,*p);  //值不一样
00007FF7D37E1967  mov         rax,qword ptr [p]  
00007FF7D37E196B  mov         r8d,dword ptr [rax]  
00007FF7D37E196E  mov         edx,dword ptr [a]  
00007FF7D37E1971  lea         rcx,[string "%d,%d\n" (07FF7D37E9CB0h)]  
00007FF7D37E1978  call        printf (07FF7D37E11E0h)  
    return 0;
00007FF7D37E197D  xor         eax,eax  
}
00007FF7D37E197F  mov         edi,eax  
00007FF7D37E1981  lea         rcx,[rbp-20h]  
}
00007FF7D37E1985  lea         rdx,[__xt_z+1E0h (07FF7D37E9C80h)]  
00007FF7D37E198C  call        _RTC_CheckStackVars (07FF7D37E1343h)  
00007FF7D37E1991  mov         eax,edi  
00007FF7D37E1993  mov         rcx,qword ptr [rbp+0F8h]  
00007FF7D37E199A  xor         rcx,rbp  
00007FF7D37E199D  call        __security_check_cookie (07FF7D37E10DCh)  
00007FF7D37E19A2  lea         rsp,[rbp+108h]  
00007FF7D37E19A9  pop         rdi  
00007FF7D37E19AA  pop         rbp  
00007FF7D37E19AB  ret  

VS调试生成汇编代码的方式:
先添加断点,然后进行调试,然后在“调试”->“窗口”->“反汇编”.这样才能看见反汇编选项。如果没有增加断点是看不见的。查看寄存器、内存等也是一样的。
在这里插入图片描述
当然也可以通过cmd命令行形式直接输出反汇编后的代码:
例(需要注意生成的.s文件需要注明详细地址,用txt文本文件格式打开即可):

C:\Users\Administrator>g++ -S C:\\Users\\Administrator\\Desktop\\testc++.cpp  -o C:\\Users\\Administrator\\Desktop\\啦啦啦.s

在这里插入图片描述
当然我们也可以通过在线编译器来查看反汇编代码和输出结果:
推荐两个网址(实际在线编译器众多,这里只挑出有特色的的(并不一定是最好的,只有适合的才是最好的)):
C++ Shell
C++ Shell 系统使用的是 GCC 4.9.2,并带有 Boost 1.55。它具有语法高亮、错误提示等功能。此外,它还支持一些额外的选项,像 C++ 标准选择(C++98/C++11/C++14)、警告级别、优化级别、标准输入等。
虽然缺点相对较多,比如缺少智能提示、创建文件/项目、下载代码、自定义设置等功能,而且执行速度也较慢,但是警告级别、优化级别功能还是OK的。
在这里插入图片描述

Compiler Explorer
Compiler Explorer 是一个交互式编译器,左侧显示了可编辑的 C/C++、Go、Swift(以及更多)代码,右侧是编译代码后的程序集输出,比较适合用来查看汇编代码。它具有代码高亮、自定义设置、错误提示、汇编输出、保存、共享等功能。
它的缺点是没有智能提示,而且功能有点儿多,让人眼花缭乱!但查看汇编语言等功能很OK。
在这里插入图片描述

posted @ 2021-01-31 15:53  study-hard-forever  阅读(293)  评论(0编辑  收藏  举报