1、课程目标
- 掌握C/C++中内联汇编的使用方法
- 理解内联汇编的限制和注意事项
- 学会C与汇编的混合链接
- 掌握跨语言调用技术
2、名词解释
| 术语 |
英文 |
说明 |
| 内联汇编 |
Inline Assembly |
嵌入在C/C++代码中的汇编代码 |
| __asm |
Asm Keyword |
MSVC内联汇编关键字 |
| 混合编程 |
Mixed Programming |
C/C++与汇编联合编程 |
| 名称修饰 |
Name Mangling |
C++编译器对函数名的编码 |
| extern “C” |
C Linkage |
使用C语言链接方式 |
3、使用工具
| 工具 |
用途 |
| MSVC (x86) |
支持内联汇编的编译器 |
| MASM |
单独的汇编模块 |
| Dumpbin |
查看导出符号 |
4、技术原理
4.1、内联汇编限制
| 平台 |
MSVC支持 |
| x86 |
完全支持__asm |
| x64 |
不支持内联汇编 |
| ARM |
不支持内联汇编 |
4.2、混合编程方式
- 内联汇编 - 仅x86
- 单独.asm文件 - 所有平台
- Intrinsic函数 - 所有平台
5、代码实现
5.1、示例1:基本内联汇编
#include <stdio.h>
// 基本内联汇编语法
int Add(int a, int b) {
int result;
__asm {
mov eax, a
add eax, b
mov result, eax
}
return result;
}
// 访问局部变量和参数
int Calculate(int x, int y, int z) {
int temp;
__asm {
mov eax, x ; 直接使用参数名
imul eax, y ; x * y
mov temp, eax ; 保存中间结果
add eax, z ; + z
}
return temp + z;
}
// 调用C函数
void __cdecl PrintValue(int val);
void TestCall() {
__asm {
push 100
call PrintValue
add esp, 4
}
}
int main() {
printf("Add(10, 20) = %d\n", Add(10, 20));
printf("Calculate(3, 4, 5) = %d\n", Calculate(3, 4, 5));
return 0;
}
void __cdecl PrintValue(int val) {
printf("Value: %d\n", val);
}
5.2、示例2:寄存器操作
#include <stdio.h>
#include <windows.h>
// 获取CPU信息
void GetCPUID(int function, int* eax, int* ebx, int* ecx, int* edx) {
__asm {
mov esi, eax ; 保存指针
mov edi, ebx
mov eax, function
cpuid
; 保存结果
push eax
push ebx
push ecx
push edx
mov eax, [esp+12] ; 原始eax
mov edi, esi ; eax指针
mov [edi], eax
; ...继续处理其他寄存器
add esp, 16
}
}
// 读取时间戳计数器
unsigned __int64 ReadTSC() {
unsigned int lo, hi;
__asm {
rdtsc ; 读取时间戳
mov lo, eax
mov hi, edx
}
return ((unsigned __int64)hi << 32) | lo;
}
// 实现原子操作
long AtomicIncrement(long* value) {
long result;
__asm {
mov ecx, value
mov eax, 1
lock xadd [ecx], eax ; 原子加
inc eax ; 返回新值
mov result, eax
}
return result;
}
int main() {
printf("TSC: %llu\n", ReadTSC());
long val = 10;
printf("Before: %ld\n", val);
printf("After increment: %ld\n", AtomicIncrement(&val));
printf("Final: %ld\n", val);
return 0;
}
5.3、示例3:汇编模块与C链接
// main.c
#include <stdio.h>
// 声明外部汇编函数
extern int __cdecl AsmAdd(int a, int b);
extern int __stdcall AsmMultiply(int a, int b);
extern void __cdecl AsmMemcpy(void* dest, const void* src, size_t n);
int main() {
printf("AsmAdd: %d\n", AsmAdd(100, 200));
printf("AsmMultiply: %d\n", AsmMultiply(12, 12));
char src[] = "Hello World";
char dest[32] = {0};
AsmMemcpy(dest, src, sizeof(src));
printf("Copied: %s\n", dest);
return 0;
}
; asmmodule.asm
.386
.model flat, C ; C调用约定
option casemap:none
.code
; int __cdecl AsmAdd(int a, int b)
AsmAdd PROC
push ebp
mov ebp, esp
mov eax, [ebp+8] ; a
add eax, [ebp+12] ; + b
pop ebp
ret ; cdecl: 调用者清栈
AsmAdd ENDP
; int __stdcall AsmMultiply(int a, int b)
; 注意: stdcall需要用@后缀或在.model中指定
AsmMultiply PROC
push ebp
mov ebp, esp
mov eax, [ebp+8]
imul eax, [ebp+12]
pop ebp
ret 8 ; stdcall: 被调用者清栈
AsmMultiply ENDP
; void __cdecl AsmMemcpy(void* dest, const void* src, size_t n)
AsmMemcpy PROC
push ebp
mov ebp, esp
push esi
push edi
mov edi, [ebp+8] ; dest
mov esi, [ebp+12] ; src
mov ecx, [ebp+16] ; n
cld
rep movsb
pop edi
pop esi
pop ebp
ret
AsmMemcpy ENDP
END
5.4、示例4:C++与汇编混合
// cppmodule.cpp
#include <iostream>
// 使用extern "C"避免名称修饰
extern "C" {
int AsmFunction(int x);
void CallCppFromAsm(int value);
}
// C++函数,供汇编调用
void CallCppFromAsm(int value) {
std::cout << "Called from ASM with: " << value << std::endl;
}
class Calculator {
public:
int Add(int a, int b) {
int result;
__asm {
mov eax, a
add eax, b
mov result, eax
}
return result;
}
// 使用this指针
int GetValue() {
int* pValue = &m_value;
int result;
__asm {
mov ecx, pValue
mov eax, [ecx]
mov result, eax
}
return result;
}
private:
int m_value = 42;
};
int main() {
Calculator calc;
std::cout << "Add: " << calc.Add(10, 20) << std::endl;
std::cout << "Value: " << calc.GetValue() << std::endl;
return 0;
}
5.5、示例5:x64混合编程方案
// x64不支持内联汇编,使用单独.asm文件
// main_x64.c
#include <stdio.h>
#include <intrin.h> // Intrinsic函数
// 声明外部汇编函数
extern __int64 AsmAdd64(__int64 a, __int64 b);
extern void AsmMemset64(void* dest, int value, size_t count);
// 使用Intrinsic代替内联汇编
void IntrinsicExample() {
// 读取TSC
unsigned __int64 tsc = __rdtsc();
printf("TSC: %llu\n", tsc);
// CPUID
int cpuInfo[4];
__cpuid(cpuInfo, 0);
printf("CPUID: EAX=%08X\n", cpuInfo[0]);
// 字节交换
unsigned short val = 0x1234;
val = _byteswap_ushort(val);
printf("Swapped: %04X\n", val);
// 位扫描
unsigned long index;
unsigned char found = _BitScanForward(&index, 0x80);
if (found) printf("First bit at: %lu\n", index);
}
int main() {
IntrinsicExample();
printf("AsmAdd64: %lld\n", AsmAdd64(100, 200));
return 0;
}
; x64_module.asm
; ml64 /c x64_module.asm
.code
; __int64 AsmAdd64(__int64 a, __int64 b)
; RCX = a, RDX = b
AsmAdd64 PROC
mov rax, rcx
add rax, rdx
ret
AsmAdd64 ENDP
; void AsmMemset64(void* dest, int value, size_t count)
; RCX = dest, EDX = value, R8 = count
AsmMemset64 PROC
push rdi
mov rdi, rcx ; dest
mov eax, edx ; value (BYTE)
mov rcx, r8 ; count
cld
rep stosb
pop rdi
ret
AsmMemset64 ENDP
END
6、课后作业
- 内联汇编练习:用内联汇编实现常用的位操作函数
- 混合链接:创建一个C/汇编混合项目,实现字符串处理函数
- x64适配:将x86内联汇编代码改写为x64单独模块
- 性能对比:比较内联汇编和Intrinsic的性能差异