C++内嵌汇编示例详解

 更新时间:2022年01月20日 17:08:35   作者:计算机知识杂谈  
这篇文章主要介绍了C++内嵌汇编,本文的所有代码是在我自己的VS2008中测试的,由于环境的差别,不能保证能在所有的编译器上运行,需要的朋友可以参考下

汇编语言

汇编语言是一种功能很强的程序设计语言,也是利用了计算机所有硬件特性并能直接控制硬件的语言。在汇编语言中,用助记符(Memoni)代替操作码,用地址符号(Symbol)或标号(Label)代替地址码。这样用符号代替机器语言的二进制码,就把机器语言变成了汇编语言。

汇编语言比机器语言易于读写、调试和修改,同时也具有机器语言执行速度快、占用内存空间少等优点。但在编写复杂程序时,相对高级语言来说汇编语言代码量较大,而且汇编语言依赖于具体的机型,不能通用,因此不能直接在不同处理机型之间移植。虽然其移植性不好,但效率非常高,针对计算机特定硬件而编制的汇编语言程序,能准确地发挥计算机硬件的功能和特长,程序精炼而质量高,所以汇编语言至今仍是一种常用而强有力的底层开发语言。

汇编语言的特点

汇编语言指令使用一些具有相应含义的助忆符来表达的,所以,它要比机器语言容易掌握和运用。但因为要直接使用CPU资源,所以相对高级程序设计语言来说它又显得相对复杂。汇编语言程序归纳起来大概有以下几个主要特点。

  1. 与硬件相关:汇编语言指令是指机器指令的一种符号表示,而不同类型的CPU有不同的机器指令系统,也就有不同的汇编语言,所以汇编语言程序与机器有着密切的关系。也就是说,不同型号的CPU之间是无法通用相同汇编代码的,因此导致汇编语言的移植性和通用性降低,这是汇编语言天生的缺陷。

  2. 保持了机器语言的优点,具有直接和简捷的特点:正因为汇编语言有“与机器相关性”的特性,程序员用汇编语言编写程序时,可充分发挥自己的聪明才智,对机器内部的各种资源进行合理的安排,让它们始终处于最佳的使用状态,这样做的最终效果就是程序的执行代码短,执行速度快,所以,汇编语言是高效的程序设计语言。另外汇编语言可有效地访问、控制计算机的各种硬件设备,如磁盘、存储器、CPU、I/O端口等,实现资源利用的最大化。

  3. 编写程序复杂:汇编语言是一种面向机器的语言,其汇编指令与机器指令基本上一一对应,所以,汇编指令也同机器指令一样既有功能单一、具体的特点。要想完成某件工作,就必须安排CPU的每步工作。另外,在编写汇编语言程序时,还要考虑具体机型的限制、汇编指令的细节和限制等。

  4. 经常与高级语言配合使用,应用十分广泛:在某些情况下,比如直接操作CPU执行中断以实现线程调度、保存CPU寄存器以存储/恢复线程状态等,仅仅使用高级语言是完不成的,需要借助于汇编语言,但是仅使用汇编语言的话,大型程序恐怕需要付出比高级语言几倍的工作量,有时候也是没有必要的。因此,可以在高级语言里嵌入汇编语句,让仅仅一部分需要高效率的代码用汇编语言来完成,其余的框架搭建等用高级语言来完成,这样既保证了效率又降低了代码的复杂程度。这种配合使用在大型软件开发里经常遇到,应用十分广泛。

注:
本文的所有代码是在我自己的VS2008中测试的,由于环境的差别,不能保证能在所有的编译器上运行。

1.内嵌汇编介绍

在C++中,可以通过__asm关键字来嵌入汇编语言。
例如

int main(){
  __asm{//汇编!
    mov eax,0
  }
  return 0;
}

2.汇编版本Hello, World!

我们知道,在C++中,可以使用printf函数来输出。(如果使用cout,需要使用运算符重载等技术,在这里反而不方便)

提示:
汇编中,调用函数的指令叫做CALL。
函数的参数是保存在栈中的。

那么我们可以开始写了。首先,先看看C++正常版本的:

#include<stdio.h>
#include<stdlib.h>

const char *s1="Hello, World\n",*s2="pause";
int main(){
    printf(s1);
    system(s2);
    
    return 0;
}

为了方便,我们先把正常版本反汇编一下,结果是:

printf(s1);
00BD13CE  mov         esi,esp 
00BD13D0  mov         eax,dword ptr [s1 (0BD7038h)] 
00BD13D5  push        eax  
00BD13D6  call        dword ptr [__imp__printf (0BD82C4h)] 
00BD13DC  add         esp,4 
00BD13DF  cmp         esi,esp 
00BD13E1  call        @ILT+315(__RTC_CheckEsp) (0BD1140h) 

第一句,mov esi,esp 为了后面检查栈是否正常用
第二句,mov eax,dword ptr[s1] 括号中的0BD7038h是地址,不要管他,意思是把地址放到eax中去
第三句,push eax 把刚才放进eax的地址放入栈, 实际就是把参数放入栈

第四句,call dword ptr [__imp__printf]
__imp__printf是printf函数编译后的结果,下划线开头表示这是一个函数
我们平时写内联汇编的时候直接写printf即可

第五句,add esp,4
其实是手动平栈,之前往栈里面放了4字节的s1,现在把esp指针也就是栈顶指针下移(栈从高地址往低地址),平栈

最后两句不管它,就是保证esi和esp相等,因为之前手动平了栈,结合第一句,这里应该是相等的,不写应该也没事

最终的内联汇编应该是这样:

#include<stdio.h>
#include<stdlib.h>

const char *s1="Hello, World\n",*s2="pause";
int main(){
    _asm{
        mov eax,dword ptr [s1]
        push eax
        call dword ptr [printf]
        add esp,4
        mov eax,dword ptr[s2]
        push eax
        call dword ptr [system]
        add esp,4
    }
    return 0;
}

运行结果正常。

3.内联汇编A+B

A+B问题,同时需要使用scanf和printf

首先注意一点,函数的参数在栈中是倒着存放的。(注:这个C标准没有规定,但是汇编语言本身就是非常依赖环境的一个东西,所以暂且不管它)

例如

scanf("%d %d",&a,&b);

如果翻译成汇编,应该是这样(下面的是伪代码)

push &b
push &a
push "%d %d"
call scanf

然后我们就可以开始写了。

scanf的部分,注意最前面两个参数,由于放入的是地址,所以不能使用MOV指令而是要使用LEA指令

lea eax,[a]

表示把a的地址放入eax中。

其他部分没有什么难度,注意最后平栈的时候,add esp到底加上多少,加上的是每个参数的大小相加。

例如scanf,每个都是4字节的地址,总共就是12字节。

完整代码

#include<stdio.h>
#include<stdlib.h>
const char *s1="%d%d",*s2="%d\n",*s3="pause";
int a,b;
int main(){
    _asm{
        lea eax,[b]
        push eax
        lea eax,[a]
        push eax
        mov eax,dword ptr [s1]
        push eax
        call dword ptr [scanf]
        add esp,12
        mov eax,[a]
        add eax,[b]
        push eax
        mov eax,dword ptr [s2]
        push eax
        call dword ptr [printf]
        add esp,8
        mov eax,dword ptr [s3]
        push eax
        call dword ptr [system]
        add esp,4
    }
    return 0;
}

到此这篇关于C++内嵌汇编的文章就介绍到这了,更多相关C++内嵌汇编内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • c++打印封装每次打印前面加上时间戳问题

    c++打印封装每次打印前面加上时间戳问题

    这篇文章主要介绍了c++打印封装每次打印前面加上时间戳问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • C++11利用原子操作实现自旋锁

    C++11利用原子操作实现自旋锁

    C++自旋锁是一种低层次的同步原语,用于保护共享资源的访问,这篇文章主要为大家介绍了如何利用原子操作实现自旋锁,感兴趣的小伙伴可以了解下
    2023-09-09
  • C语言直接插入排序算法

    C语言直接插入排序算法

    大家好,本篇文章主要讲的是C语言直接插入排序算法,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2022-01-01
  • C++实现简单贪吃蛇游戏

    C++实现简单贪吃蛇游戏

    这篇文章主要为大家详细介绍了C++实现简单贪吃蛇游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • C语言实现通讯录功能的流程与代码

    C语言实现通讯录功能的流程与代码

    通讯录是一个可以记录亲人、好友信息的工具,这篇文章主要为大家详细介绍了C语言实现通讯录管理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • C++ Primer学习记录之变量

    C++ Primer学习记录之变量

    这篇文章主要为大家介绍了C++Primer之变量,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01
  • C/C++ Qt TabWidget 实现多窗体创建详解

    C/C++ Qt TabWidget 实现多窗体创建详解

    TabWidget组件配合自定义Dialog组件,可实现一个复杂的多窗体分页结构。这篇文章就主要介绍了如何通过TabWidget实现多窗体的创建,感兴趣的小伙伴可以了解一下
    2021-12-12
  • 基于字符串移位包含的问题详解

    基于字符串移位包含的问题详解

    本篇文章是对字符串移位包含的问题的解决方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C语言实现制作通讯录(新手推荐)

    C语言实现制作通讯录(新手推荐)

    本文推荐给C语言学习到结构体的新手们,供其练习。这篇文章主要是利用C语言制作一个简单的通讯录功能,感兴趣的小伙伴可以跟随小编一起了解一下
    2022-09-09
  • ShellExecute函数用法的实例代码

    ShellExecute函数用法的实例代码

    ShellExecute函数用法的实例代码,需要的朋友可以参考一下
    2013-03-03

最新评论