📄 csdn技术中心 在visual c++中使用内联汇编.htm
字号:
{ <BR> MOV EBX, OFFSET
hal <BR> MOV ECX,
[EBX]hal.same_name ; 必须使用 'hal' <BR>
MOV ESI, [EBX].weasel ; 可以省略 'hal'
<BR> } <BR><BR>
注意,省略了变量名仅仅是为了写代码的方便,生成的汇编指令的还是一样的。 <BR><BR>
可以不受限制地访问C++成员变量,但是不能调用C++的成员函数。 <BR><BR><BR><BR><FONT
color=#ff8c00>五、寄存器使用</FONT> <BR><BR>
一般来说,在__asm块开始的时候,寄存器是空的,不能在两个__asm之间保存寄存器的值。(这是MSDN上说的,我在实际使用时发现,好像并不是这样。不过它是说"一般",我是特殊:))
<BR><BR>
如果一个函数被声明成了__fastcall,则其参数将放在寄存器中,这将给寄存器的管理带来问题。所以,如果要将一个函数声明成__fastcall,必须保存ECX寄存器。为了避免以上的冲突,在声明为__fastcall的函数中不要有__asm块。如果用了/Gr编译选项(它全局的变成__fastcall),将每个函数声明成__cdecl或者__stdcall,这个属性告诉编译器用传统的C方法。
<BR><BR>
如果使用EAX、EBX、ECX、EDX、ESI和EDI寄存器,你不需要保存它;但如果你用到了DS、
SS、SP、BP和标志寄存器,那就应该PUSH保存这些寄存器。 <BR><BR>
如果程序中改变了用于STD和CLD的方向标志,你必须将其恢复到原来的值。 <BR><BR><BR><BR><BR><FONT
color=#ff8c00>六、转跳</FONT> <BR><BR>
可以在C里面使用goto调到__asm块中的标号处,也可以在__asm块中转跳到__asm块里面和外面的标号处。__asm块内的标号是不区分大小写的(指令、指示符等也是不区分大小写的)。例:
<BR><BR> void func() <BR>
{ <BR>
goto C_Dest; /* 合法 */ <BR>
goto c_dest; /* 错误 */
<BR><BR> goto
A_Dest; /* 合法 */ <BR>
goto a_dest; /* 合法 */
<BR><BR> __asm
<BR> {
<BR>
JMP C_Dest ; 合法 <BR>
JMP
c_dest ; MSDN上说合法,但是我在VS.NET中编译,认为这样不合法 <BR><BR>
JMP A_Dest ; 合法 <BR>
JMP a_dest ;
合法 <BR><BR> a_dest: ; __asm
标号 <BR> }
<BR><BR> C_Dest: /* C的标号 */
<BR> return; <BR>
} <BR><BR><BR>
不要使用函数名称当作标号,否则将使其跳到函数执行而不是标号处。如下所示: <BR><BR>
; 错误: 使用函数名作为标号 <BR> JNE exit
<BR> . <BR> .
<BR> . <BR>
exit: <BR> ; 下面是更多的ASM代码
<BR><BR><BR> 美元符号$用于指定当前位置,如下所用,常用于条件跳转: <BR><BR>
JNE $+5 ; 下面这条指令的长度是5个字节 <BR>
JMP farlabel <BR> ;$+5,跳到了这里
<BR> . <BR> .
<BR> . <BR>
farlabel: <BR><BR><BR><BR><FONT color=#ff8c00>七、调用函数</FONT>
<BR><BR> 内联汇编调用C/C++函数必须自己清除堆栈,下面是一个调用C/C++函数例子:
<BR><BR> #include <stdio.h>
<BR><BR> char szformat[] = "%s %s\n";
<BR> char szHello[] = "Hello"; <BR>
char szWorld[] = " world"; <BR>
void main() <BR> { <BR>
__asm <BR>
{ <BR>
MOV EAX, OFFSET szWorld <BR>
PUSH EAX
<BR> MOV
EAX, OFFSET szHello <BR>
PUSH EAX <BR>
MOV EAX,
OFFSET szformat <BR>
PUSH EAX <BR>
CALL printf <BR><BR>
//内联汇编调用C函数必须自己清除堆栈
<BR>
//用不使用的EBX寄存器清除堆栈,或ADD ESP, 12 <BR>
POP EBX <BR>
POP EBX
<BR> POP
EBX <BR> }
<BR> } <BR><BR>
注意:函数参数是从右向左压栈。 <BR><BR>
不能够访问C++中的类成员函数,但是可以访问extern "C"函数。 <BR><BR>
如果调用Windows API函数,则不需要自己清除堆栈,因为API的返回指令是RET n,会自动清除堆栈
<BR><BR> 比如下面的例子: <BR><BR>
#include <windows.h> <BR><BR>
char szAppName[] = "API Test"; <BR><BR>
void main() <BR> { <BR>
char szHello[] = "Hello, world!";
<BR><BR> __asm <BR>
{ <BR>
PUSH MB_OK OR
MB_ICONINformATION <BR>
PUSH OFFSET szAppName ;
全局变量用OFFSET <BR>
LEA EAX, szHello ;
局部变量用LEA <BR>
PUSH EAX <BR>
PUSH 0 <BR>
CALL DWORD PTR
[MessageBoxA] ; 注意这里,我费了好大周折才发现不是CALL MessageBoxA
<BR> } <BR>
} <BR><BR> 一般来说,在Visual
C++中使用内联汇编是为了提高速度,因此这些函数调用尽可能用C/C++写。 <BR><BR><BR><BR><FONT
color=#ff8c00>八、一个例子</FONT> <BR><BR>
下面的例子是在VS.NET(即VC7)中C语言写的。先建一个工程,将下列代码放到工程中的.c文件中编译,无需作特别的设置,即可编译通过。
<BR><BR><FONT color=#888888>
////////////////////////////////////////////////////////////////////////////////////////////////////
<BR> //预处理 <BR> #include <Windows.h>
<BR>
////////////////////////////////////////////////////////////////////////////////////////////////////
<BR><BR><BR>
////////////////////////////////////////////////////////////////////////////////////////////////////
<BR> //全局变量 <BR> HWND g_hWnd; <BR>
HINSTANCE g_hInst; <BR><BR> TCHAR szTemp[1024];
<BR><BR> TCHAR szAppName[] = "CRC32 Sample"; <BR>
////////////////////////////////////////////////////////////////////////////////////////////////////
<BR><BR><BR>
////////////////////////////////////////////////////////////////////////////////////////////////////
<BR> //函数声明 <BR> DWORD GetCRC32(const BYTE
*pbData, int nSize); <BR> int WINAPI WinMain(HINSTANCE
hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow);
<BR> LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg,
WPARAM wParam, LPARAM lParam); <BR>
////////////////////////////////////////////////////////////////////////////////////////////////////
<BR><BR><BR><BR><BR>
////////////////////////////////////////////////////////////////////////////////////////////////////
<BR> //主函数 <BR> int WINAPI WinMain(HINSTANCE
hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
<BR> { <BR> MSG msg;
<BR> WNDCLASSEX wndClassEx; <BR><BR>
g_hInst = hInstance; <BR><BR>
wndClassEx.cbSize = sizeof(WNDCLASSEX); <BR>
wndClassEx.style = CS_VREDRAW | CS_HREDRAW;
<BR> wndClassEx.lpfnWndProc = (WNDPROC)
WindowProc; <BR> wndClassEx.cbClsExtra =
0; <BR> wndClassEx.cbWndExtra = 0;
<BR> wndClassEx.hInstance = g_hInst;
<BR> wndClassEx.hIcon = LoadIcon(NULL,
IDI_APPLICATION); <BR> wndClassEx.hCursor
= LoadCursor(NULL, IDC_ARROW); <BR>
wndClassEx.hbrBackground = (HBRUSH) (COLOR_WINDOW); <BR>
wndClassEx.lpszMenuName = NULL; <BR>
wndClassEx.lpszClassName = szAppName; <BR>
wndClassEx.hIconSm = NULL; <BR><BR>
RegisterClassEx(&wndClassEx); <BR><BR>
g_hWnd = CreateWindowEx(0, szAppName, szAppName,
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME |
WS_MINIMIZEBOX, <BR>
CW_USEDEFAULT, CW_USEDEFAULT, 300, 70, <BR>
NULL, NULL, g_hInst, NULL);
<BR><BR> ShowWindow(g_hWnd, iCmdShow);
<BR> UpdateWindow(g_hWnd); <BR><BR>
while (GetMessage(&msg, NULL, 0, 0))
<BR> { <BR>
TranslateMessage(&msg); <BR>
DispatchMessage(&msg); <BR>
} <BR> return ((int)
msg.wParam); <BR> } <BR>
////////////////////////////////////////////////////////////////////////////////////////////////////
<BR><BR><BR><BR>
////////////////////////////////////////////////////////////////////////////////////////////////////
<BR> //主窗口回调函数 <BR> LRESULT CALLBACK
WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
<BR> { <BR> switch (uMsg)
<BR> { <BR> case
WM_CREATE: <BR>
CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD |
WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL | ES_AUTOVSCROLL |
ES_NOHIDESEL | WS_OVERLAPPED, <BR>
7, 12, 220, 22, <BR>
hWnd, (HMENU)1000, g_hInst, NULL);
<BR> CreateWindowEx(0,
"BUTTON", "&OK", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON |
WS_OVERLAPPED | BS_FLAT, <BR>
244, 12, 40, 20, <BR>
hWnd, (HMENU)IDOK, g_hInst, NULL);
<BR><BR> break;
<BR><BR> case WM_COMMAND: <BR>
switch (LOWORD(wParam)) <BR>
{ <BR>
case IDOK: <BR>
GetDlgItemText(g_hWnd, 1000, szTemp + 100, 800);
<BR>
wsprintf(szTemp, "当前文本框内的字符串的CRC32校验码是: 0x%lX",
GetCRC32(szTemp + 100, (int)strlen(szTemp + 100))); <BR>
MessageBox(g_hWnd,
szTemp, szAppName, MB_OK|MB_ICONINformATION); <BR>
} <BR>
break; <BR><BR> case WM_DESTROY:
<BR> PostQuitMessage(0);
<BR> break; <BR><BR>
default: <BR>
return (DefWindowProc(hWnd, uMsg, wParam, lParam)); <BR>
} <BR> return (0);
<BR> } <BR>
////////////////////////////////////////////////////////////////////////////////////////////////////
<BR><BR><BR><BR></FONT><BR><FONT color=#32cd32>
////////////////////////////////////////////////////////////////////////////////////////////////////
<BR> //GetCRC32: 求字节流的CRC32校验码 <BR> //参数:
<BR> // pbData: 指向字节流缓冲区首地址
<BR> // nSize: 字节流长度 <BR>
// <BR> //返回值: <BR> //
字节流的CRC32校验码 <BR> // <BR>
//这里使用查表法求CRC32校验码,这部分是参考老罗的文章《 矛与盾的较量(2)——CRC原理篇》该写的。
<BR> //原文的具体内容请参看: <A
href="http://asp.7i24.com/netcool/laoluo/articles/show_article.asp?Article_ID=15"
target=_blank>http://asp.7i24.com/netcool/laoluo/articles/show_article.asp?Article_ID=15</A>
<BR> // <BR>
//下面使用内联汇编求CRC32校验码,充分使用了CPU中的寄存器,速度和方便性都是使用C/C++所不能比拟的
<BR> //</FONT> <BR><FONT color=#6495ed>
DWORD GetCRC32(const BYTE *pbData, int nSize) <BR>
{ <BR> DWORD dwCRC32Table[256];
<BR><BR> __asm //这片内联汇编是初始化CRC32表
<BR> { <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -