OFFSET hal
?????MOV ECX, [EBX]hal.same_name ; 必须使用 ’hal’
?????MOV ESI, [EBX].weasel??? ; 可以省略 ’hal’
???}
?注意
,省略了变量名仅仅是为了写代码的方便
,生成的汇编指令的还是一样的
。 ?可以不受限制地访问C++成员变量,但是不能调用C++的成员函数
。 五、寄存器使用
?一般来说,在__asm块开始的时候,寄存器是空的,不能在两个__asm之间保存寄存器的值。(这是MSDN上说的,我在实际使用时发现,好像并不是这样。不过它是说"一般",我是特殊:))
?如果一个函数被声明成了__fastcall,则其参数将放在寄存器中,这将给寄存器的管理带来问题。所以,如果要将一个函数声明成__fastcall,必须保存ECX寄存器。为了避免以上的冲突,在声明为__fastcall的函数中不要有__asm块。如果用了/Gr编译选项(它全局的变成__fastcall),将每个函数声明成__cdecl或者__stdcall,这个属性告诉编译器用传统的C方法。
?如果使用EAX、EBX、ECX、EDX、ESI和EDI寄存器,你不需要保存它;但如果你用到了DS、 SS、SP、BP和标志寄存器,那就应该PUSH保存这些寄存器。
?如果程序中改变了用于STD和CLD的方向标志,你必须将其恢复到原来的值。
六、转跳
?可以在C里面使用goto调到__asm块中的标号处,也可以在__asm块中转跳到__asm块里面和外面的标号处。__asm块内的标号是不区分大小写的(指令、指示符等也是不区分大小写的)。例:
???void func()
???{
???????goto C_Dest;??/* 合法 */
???????goto c_dest;??/* 错误 */
???????goto A_Dest;??/* 合法 */
???????goto a_dest;??/* 合法 */
???????__asm
???????{
???????????JMP C_Dest?; 合法
???????????JMP c_dest?; MSDN上说合法,但是我在VS.NET中编译,认为这样不合法
???????????JMP A_Dest?; 合法
???????????JMP a_dest?; 合法
???a_dest:?? ; __asm 标号
???????}
???C_Dest:?? /* C的标号 */
???return;
???}
?不要使用函数名称当作标号,否则将使其跳到函数执行而不是标号处。如下所示:
???; 错误: 使用函数名作为标号
???JNE exit
???.
???.
???.
???exit:
???; 下面是更多的ASM代码
?美元符号$用于指定当前位置,如下所用,常用于条件跳转:
???JNE $Content$5 ; 下面这条指令的长度是5个字节
???JMP farlabel
???;$Content$5,跳到了这里
???.
???.
???.
???farlabel:
七、调用函数
?内联汇编调用C/C++函数必须自己清除堆栈,下面是一个调用C/C++函数例子:
???#include <stdio.h>
???char szformat[] = "%s %s
";
???char szHello[] = "Hello";
???char szWorld[] = " world";
???void main()
???{
?????__asm
?????{
???????MOV?? EAX, OFFSET szWorld
???????PUSH??EAX
???????MOV?? EAX, OFFSET szHello
???????PUSH??EAX
???????MOV?? EAX, OFFSET szformat
???????PUSH??EAX
???????CALL??printf
???????//内联汇编调用C函数必须自己清除堆栈
???????//用不使用的EBX寄存器清除堆栈,或ADD ESP, 12
???????POP?? EBX
???????POP?? EBX
???????POP?? EBX
?????}
???}