在vsualc中使用内联

上传人:第*** 文档编号:61926977 上传时间:2018-12-15 格式:PDF 页数:13 大小:228.60KB
返回 下载 相关 举报
在vsualc中使用内联_第1页
第1页 / 共13页
在vsualc中使用内联_第2页
第2页 / 共13页
在vsualc中使用内联_第3页
第3页 / 共13页
在vsualc中使用内联_第4页
第4页 / 共13页
在vsualc中使用内联_第5页
第5页 / 共13页
点击查看更多>>
资源描述

《在vsualc中使用内联》由会员分享,可在线阅读,更多相关《在vsualc中使用内联(13页珍藏版)》请在金锄头文库上搜索。

1、在在 Visual C+ 中使用内联汇编中使用内联汇编 一、一、 优点优点 使用内联汇编可以在 C/C+ 代码中嵌入汇编语言指令,而且不需要额外的汇编和连接 步骤。在 Visual C+ 中,内联汇编是内置的编译器,因此不需要配置诸如 MASM 一类的 独立汇编工具。这里,我们就以 Visual Studio .NET 2003 为背景,介绍在 Visual C+ 中使 用内联汇的相关知识(如果是早期的版本,可能会有些许出入) 。 内联汇编代码可以使用 C/C+ 变量和函数,因此它能非常容易地整合到 C/C+ 代码 中。它能做一些对于单独使用 C/C+ 来说非常笨重或不可能完成的任务。 内联汇

2、编的用途包括: 使用汇编语言编写特定的函数; 编写对速度要求非常较高的代码; 在设备驱动程序中直接访问硬件; 编写 naked 函数的初始化和结束代码。 二、二、 关键字关键字 使用内联汇编要用到 _asm 关键字, 它可以出现在任何允许 C/C+ 语句出现的地方。 我们来看一些例子: 简单的 _asm 块: _asm MOVAL, 2 MOV DX, 0xD007 OUTAL, DX 在每条汇编指令之前加 _asm 关键字: _asm MOVAL, 2 _asm MOV DX, 0xD007 _asm OUTAL, DX 因为 _asm 关键字是语句分隔符,所以可以把多条汇编指令放在同一行:

3、 _asm MOVAL, 2_asm MOV DX, 0xD007_asm OUTAL, DX 显然,第一种方法与 C/C+ 的风格很一致,并且把汇编代码和 C/C+ 代码清楚地分 开,还避免了重复输入 _asm 关键字,因此推荐使用第一种方法。 不像在 C/C+ 中的“” ,_asm 块的“”不会影响 C/C+ 变量的作用范围。同时, _asm 块可以嵌套,而且嵌套也不会影响变量的作用范围。 为了与低版本的 Visual C+ 兼容, _asm 和 _asm 具有相同的意义。 另外, Visual C+ 支持标准 C+ 的 asm 关键字,但是它不会生成任何指令,它的作用仅限于使编译器不会

4、出现编译错误。要使用内联汇编,必须使用 _asm 而不是 asm 关键字。 三、三、 汇编语言汇编语言 1. 指令集指令集 内联汇编支持 Intel Pentium 4 和 AMD Athlon 的所有指令。更多其它处理器的指令可 以通过 _EMIT 伪指令来创建(_EMIT 伪指令说明见下文) 。 2. MASM 表达式表达式 在内联汇编代码中,可以使用所有的 MASM 表达式(MASM 表达式是指用来计算一 个数值或一个地址的操作符和操作数的组合) 。 3. 数据指示符和操作符数据指示符和操作符 虽然 _asm 块中允许使用 C/C+ 的数据类型和对象,但它不能使用 MASM 指示符 和操

5、作符来定义数据对象。这里特别指出,_asm 块中不允许 MASM 中的定义指示符 (DB、DW、DD、DQ、DT 和 DF) ,也不允许使用 DUP 和 THIS 操作符。MASM 中的 结构和记录也不再有效,内联汇编不接受 STRUC、RECORD、WIDTH 或者 MASK。 4. EVEN 和和 ALIGN 指示符指示符 尽管内联汇编不支持大多数 MASM 指示符,但它支持 EVEN 和 ALIGN。当需要的 时候,这些指示符在汇编代码里面加入 NOP 指令(空操作)使标号对齐到特定边界。这样 可以使某些处理器取指令时具有更高的效率。 5. MASM 宏指示符宏指示符 内联汇编不是宏汇编

6、,不能使用 MASM 宏指示符(MACRO、REPT、IRC、IRP 和 ENDM)和宏操作符(、!、 下面是 C 和汇编表达式中得到的 iArray 及其元素的相关值: _asmCSize LENGTH iArraysizeof(iArray)/sizeof(iArray0)8 SIZE iArraysizeof(iArray)32 TYPE iArraysizeof(iArray0)4 8. 注释注释 内联汇编中可以使用汇编语言的注释,即“;” 。例如: _asm MOV EAX, OFFSET pbBuff; Load address of pbBuff 因为 C/C+ 宏将会展开到一个

7、逻辑行中,为了避免在宏中使用汇编语言注释带来的混 乱,内联汇编也允许使用 C/C+ 风格的注释。 9. _EMIT 伪指令伪指令 _EMIT 伪指令相当于 MASM 中的 DB,但是 _EMIT 一次只能在当前代码段(.text 段)中定义一个字节。例如: _asm JMP _CodeLabel _EMIT 0x00; 定义混合在代码段的数据 _EMIT 0x01 _CodeLabel:; 这里是代码 _EMIT 0x90; NOP 指令 10. 寄存器使用寄存器使用 一般来说,不能假定某个寄存器在 _asm 块开始的时候有已知的值。寄存器的值将不 能保证会从 _asm 块保留到另外一个 _a

8、sm 块中。 如果一个函数声明为 _fastcall 调用方式,则其参数将通过寄存器而不是堆栈来传递。 这将会使 _asm 块产生问题,因为函数无法被告知哪个参数在哪个寄存器中。如果函数接 收了 EAX 中的参数并立即储存一个值到 EAX 中的话,原来的参数将丢失掉。另外,在 所有声明为 _fastcall 的函数中,ECX 寄存器是必须一直保留的。为了避免以上的冲突, 包含 _asm 块的函数不要声明为 _fastcall 调用方式。 * 提示:如果使用 EAX、EBX、ECX、EDX、ESI 和 EDI 寄存器,你不需要保存它。 但如果你用到了 DS、SS、SP、BP 和标志寄存器,那就应

9、该用 PUSH 保存这些寄存器。 * 提示: 如果程序中改变了用于 STD 和 CLD 的方向标志, 必须将其恢复到原来的值。 四、四、 使用使用 C/C+ 元素元素 1. 可用的可用的 C/C+ 元素元素 C/C+ 与汇编语言可以混合使用, 在内联汇编中可以使用 C/C+ 变量以及很多其它的 C/C+ 元素,包括: 符号,包括标号、变量和函数名; 常量,包括符号常量和枚举型成员; 宏定义和预处理指示符; 注释,包括“/*/”和“/” ; 类型名,包括所有 MASM 中合法的类型; typedef 名称,通常使用 PTR 和 TYPE 操作符,或者使用指定的的结构或枚举成 员。 在内联汇编中,

10、可以使用 C/C+ 或汇编语言的基数计数法。例如,0x100 和 100H 是 相等的。 2. 操作符使用操作符使用 内联汇编中不能使用诸如“ int GetPowerC(int iNum, int iPower); int main() printf(“3 times 2 to the power of 5 is %dn“, GetPowerC( 3, 5); int GetPowerC(int iNum, int iPower) _asm MOV EAX, iNum; Get first argument MOV ECX, iPower; Get second argument SHL E

11、AX, CL; EAX = EAX * (2 to the power of CL) / Return with result in EAX 使用内联汇编写的 GetPowerC 函数可以通过参数名称来引用它的参数。由于 GetPowerC 函数没有执行 C 的 return 语句,所以编译器会给出一个警告信息,我们可以 通过 #pragma warning 禁止生成这个警告。 内联汇编的其中一个用途是编写 naked 函数的初始化和结束代码。对于一般的函数, 编译器会自动帮我们生成函数的初始化(构建参数指针和分配局部变量等)和结束代码(平 衡堆栈和返回一个值等) 。使用内联汇编,我们可以自己

12、编写干干净净的函数。当然,此时 我们必须自己动手做一些有关函数初始化和扫尾的工作。例如: void _declspec(naked) MyNakedFunction() / Naked functions must provide their own prolog. _asm PUSH EBP MOV ESP, EBP SUB ESP, _LOCAL_SIZE . . . /And we must provide epilog. _asm POP EBP RET 6. 调用调用 C/C+ 函数函数 内联汇编中调用声明为 _cdecl 方式(默认)的 C/C+ 函数必须由调用者清除参数堆 栈,下

13、面是一个调用 C/C+ 函数例子: #include char szFormat = “%s %sn“; char szHello = “Hello“; char szWorld = “ world“; void main() _asm MOVEAX, OFFSET szWorld PUSHEAX MOVEAX, OFFSET szHello PUSHEAX MOVEAX, OFFSET szFormat PUSHEAX CALLprintf / 压入了 3 个参数在堆栈中,调用函数之后要调整堆栈 ADDESP, 12 * 提示:参数是按从右往左的顺序压入堆栈的。 如果调用 _stdcall

14、方式的函数,则不需要自己清除堆栈。因为这种函数的返回指令是 RET n, 会自动清除堆栈。 大多数 Windows API 函数均为 _stdcall 调用方式 (仅除 wsprintf 等几个之外) ,下面是一个调用 MessageBox 函数的例子: #include TCHAR g_tszAppName = TEXT(“API Test“); void main() TCHAR tszHello = TEXT(“Hello, world!“); _asm PUSHMB_OK OR MB_ICONINFORMATION PUSHOFFSET g_tszAppName; 全局变量用 OFFS

15、ET LEAEAX, tszHello; 局部变量用 LEA PUSHEAX PUSH0 ; 注意这里不是 CALL MessageBox,而是调用重定位过的函数地址 CALLDWORD PTR MessageBox * 提示:可以不受限制地访问 C+ 成员变量,但是不能访问 C+ 的成员函数。 7. 定义定义 _asm 块为块为 C/C+ 宏宏 使用C/C+ 宏可以方便地把汇编代码插入到源代码中。但是这其中需要额外地注意, 因为宏将会扩展到一个逻辑行中。为了不会出现问题,请按以下规则编写宏: 使用括号把 _asm 块包围住; 把 _asm 关键字放在每条汇编指令之前; 使用经典 C 风格的注

16、释( “/* comment */” ),不要使用汇编风格的注释( “; comment” )或单行的 C/C+ 注释( “/ comment” ) ; 举个例子,下面定义了一个简单的宏: #define PORTIO _asm /* Port output */ _asm MOVAL, 2 _asm MOV DX, 0xD007 _asm OUT DX,AL 乍一看来,后面的三个 _asm 关键字好像是多余的。其实它们是需要的,因为宏将被 扩展到一个单行中: _asm /* Port output */ _asm MOV AL, 2_asm MOV DX, 0xD007_asm OUT DX,AL 从扩展后的代码

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 办公文档 > 解决方案

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号