C++内嵌汇编示例详解

目录
  • 汇编语言
  • 汇编语言的特点
  • 1.内嵌汇编介绍
  • 2.汇编版本Hello, World!
  • 3.内联汇编A+B

汇编语言

汇编语言是一种功能很强的程序设计语言,也是利用了计算机所有硬件特性并能直接控制硬件的语言。在汇编语言中,用助记符(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++内嵌汇编内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 从汇编看c++中的多态详解

    在c++中,当一个类含有虚函数的时候,类就具有了多态性.构造函数的一项重要功能就是初始化vptr指针,这是保证多态性的关键步骤. 构造函数初始化vptr指针 下面是c++源码: class X { private: int i; public: X(int ii) { i = ii; } virtual void set(int ii) {//虚函数 i = ii; } }; int main() { X x(1); } 下面是对应的main函数汇编码: _main PROC ; 16 : in

  • 从汇编看c++中多态的应用

    在c++中,当一个类含有虚函数的时候,类就具有了多态性.构造函数的一项重要功能就是初始化vptr指针,这是保证多态性的关键步骤.构造函数初始化vptr指针下面是c++源码: 复制代码 代码如下: class X {private:    int i;public:    X(int ii) {        i = ii;    }    virtual void set(int ii) {//虚函数        i = ii;    }};int main() {   X x(1);} 下面

  • 从汇编看c++的默认析构函数的使用详解

    c++中,如果没有为一个类提供析构函数,那么编译器会为这个类提供默认的析构的函数.由于析构函数的功能和构造函数相反,因此和默认的构造函数类似,编译器也会提供无用的默认的析构函数,和非无用的析构函数.两者的分析情况一样(对于默认的构造函数分析,请参看<从汇编看c++中默认构造函数的使用分析>).并且编译器会提供非无用的默认析构函数情形和默认构造函数类似: 1 类含有虚成员函数(类继承自虚基类或者继承的基类含有虚成员函数,也属于这种情况) 2 类继承自一个基类,基类含有自定义析构函数(如果基类没有

  • C++通过内嵌解释器调用Python及间接调用Python三方库

    目录 1.移植Python解释器 2.VS配置(VS2017为例,此教程与VS版本无关) 3.C++调用程序样例 4.被调Python程序样例 本文章目的是脱离安装Python环境的前提下,由C++程序调用Python程序及Python相关三方库 1.移植Python解释器 Python环境的目录结构 路径详解 需要用的如下图 1.红色部分是生成路径下解释器运行时依赖 将红色部分拷贝到C++编译主ExE路径下即可 2.蓝色部分是VS配置编译时依赖 路径或文件名 作用 DLLs Python内部运

  • 详解如何实现C++虚函数调用汇编代码

    虚函数(代码段地址)被存放在虚函数表中,调用虚函数的流程是这样子的:先获取虚函数表的首地址,然后根据目标虚函数在虚函数表的位置(offset偏移)取出虚函数表中的虚函数地址,最后去call这个虚函数(地址),就完成虚函数的调用.这个虚函数调用的流程在汇编代码中可以最直观的反映出来. 在排查软件异常或崩溃时,我们时常要借助汇编代码的上下文去辅助分析问题.读懂C++虚函数调用的汇编代码实现,对于搞懂汇编代码的上下文时很有好处的.今天我们就来看看虚函数调用的汇编代码实现. 比如如下的C++代码: //

  • C++内嵌汇编示例详解

    目录 汇编语言 汇编语言的特点 1.内嵌汇编介绍 2.汇编版本Hello, World! 3.内联汇编A+B 汇编语言 汇编语言是一种功能很强的程序设计语言,也是利用了计算机所有硬件特性并能直接控制硬件的语言.在汇编语言中,用助记符(Memoni)代替操作码,用地址符号(Symbol)或标号(Label)代替地址码.这样用符号代替机器语言的二进制码,就把机器语言变成了汇编语言. 汇编语言比机器语言易于读写.调试和修改,同时也具有机器语言执行速度快.占用内存空间少等优点.但在编写复杂程序时,相对高

  • ARM体系下的GCC内联汇编教程详解

    在操作系统级的编程中,有时候,C语言并不能完全的使用硬件的功能,这时候就需要嵌入一些汇编代码来实现功能. 有两种方式可以使C语言和assemly语言一起工作,一种是两种语言分开写成两个文件,链接的时候链接成一个文件;另一种就是在C语言中嵌入汇编代码.下面简单介绍一下如何在GCC中嵌入汇编代码. GCC规定了一个内联汇编的语法,不同硬件平台上的GCC内联汇编几乎都是这样的: asm( 汇编指令列表 :输出运算符列表 :输入运算符列表 :被更改的资源列表 }; 在GCC中插入汇编代码,需要以asm关

  • Flutter学习之创建一个内嵌的navigation详解

    目录 简介 搭建主Navigator 构建子路由 总结 简介 我们在flutter中可以使用Navigator.push或者Navigator.pushNamed方法来向Navigator中添加不同的页面,从而达到页面调整的目的. 一般情况下这样已经足够了,但是有时候我们有多个Navigator的情况下,上面的使用方式就不够用了.比如我们有一个主页面app的Navigator,然后里面有一个匹配好友的功能,这个功能有多个页面,因为匹配好友功能的多个页面实际上是一个完整的流程,所以这些页面需要被放

  • 手把手教你 CKEDITOR 4 实现Dialog 内嵌 IFrame操作详解

    本文实例讲述了CKEDITOR 4 实现Dialog 内嵌 IFrame操作.分享给大家供大家参考,具体如下: 在上一篇博文<CKEDITOR 4 扩展插件制作>中,姜哥跟大家探讨了在ckeditor中添加dialog,已经添加dialog的一些控件,最终将控件中的值插入到ckeditor编辑器中的过程.但是实际上,我们更多的应用,还是会在dialog中,添加iframe组件,直接引用外部网页,以实现更为复杂的功能.今天姜哥就和大家一起分享一下,这个开发过程. 继续我们上一个工程中的例子,我们

  • kotlin Standard中的内联函数示例详解

    let.with.run.apply.also.takeIf.takeUnless.repeat函数的使用 kotlin Standard.kt文件中,提供了一些内联函数,这些内联函数可以减少代码量,在使代码优美的同时,打打提高开发效率.它们分别为: run.with.let.also.apply let let函数的定义如下: public inline fun <T, R> T.let(block: (T) -> R): R = block(this) 默认当前这个对象作为闭包的it

  • three.js着色器材质的内置变量示例详解

    什么是着色器? 固定渲染管线: --标准的几何&光照(T&L)管线,功能是固定的,它控制着世界.视.投影变换及固定光照控制和纹理混合.T&L管线可以被渲染状态控制,矩阵,光照和采制参数.如果有了固定渲染管线,编写程序就比较容易了,因为所有的变换都是由固定渲染管线来完成的,但是缺点就是自由度低.固定渲染管线只能完成一些最基本的操作,如果想要做一些特殊的处理,就比较麻烦了. 可编辑渲染管线:--WebGL中不存在固定渲染管线,坐标变换必须全部由自己来做,这个记述了坐标变换的机制就叫做着

  • 常用JavaScript正则表达式汇编与示例详解

    1.1 前言 目前收集整理了21个常用的javaScript正则表达式,其中包括用户名.密码强度.整数.数字.电子邮件地址(Email).手机号码.身份证号.URL地址. IP地址. 十六进制颜色. 日期. 微信号.车牌号.中文正则等.表单验证处理必备,赶紧收藏吧! 还会陆续加入新的正则进来,大家多提宝贵意见! 2.1 用户名正则 2.1.1 基本用户名正则 在做用户注册时,都会用到用户名正则校验. 定义基本用户名命名规则如下: 最短4位,最长16位 {4,16} 可以包含小写大母 [a-z]

  • MySql中子查询内查询示例详解

    西北望乡何处是,东南见月几回圆. 月亮又慢悠悠的挂上了天空,趁着睡前梦呓,我就带领各位可爱的读者们探索MySql最后的子查询部分. 说明:有些查询结果出来结果截图与题目要求不一样会出现多余的字段是为了方便展示结果的可读性.实际操作的读者可以删除SELECT后面多余的字段得到正确的结果. #WHERE或HAVING后面 #1.标量子查询(单行子查询) #2.列子查询(多行子查询) #3.行子查询(多列多行) #特点: # ①子查询放在小括号内 # ②子查询一般放在条件的右侧 # ③标量子查询:一般

  • Elasticsearch Analyzer 内置分词器使用示例详解

    目录 前置知识 1.Analyzer 2.Elasticsearch 内置分词器 3. Standard Analyzer 3.1 Definition 3.2 Configuration 3.3 实验 4. Simple Analyzer 4.1 Definition 4.2 Configuation 4.3 实验 5. Stop Analyzer 5.1 Definition 5.2 Configuration 5.3 实验 6. Whitespace Analyzer 6.1 Defini

  • 语言编程花絮内建构建顺序示例详解

    目录 1 构建 顺序 1.1 交叉编译 1.2 设置 2 构建测试支持 1 构建 顺序 依据词法名顺序 当导入一个包,且这个包 定义了 init(), 那么导入时init()将被执行. 具体执行顺序: 全局变量定义时的函数 import 执行导入 -> cont 执行常量 --> var 执行变量 --> 执行初始化 init() --> 执行 main() ----> main import pk1 ---> pk1 const ... import pk2 ---&

随机推荐