C语言内嵌汇编代码
嵌入语法
先看一个简单的加法,代码如下。
1 |
|
__stdcall约束了函数传参顺序,保证以从右到左的顺序对参数进行压栈。上例中入栈顺序b,a,最后a在栈顶,b在栈底。
这里给出在C语言中嵌入汇编代码的一种形式:
1
2
3__asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify);
//也可简化为如下形式
__asm("Instruction List" : Output : Input : Clobber/Modify);volatile可选,选用即向GCC编译器声明不对汇编代码进行优化,不选用则GCC会自动决定优化与否;
“Instruction List”以字符串形式显示指令,一对引号包含一条指令,在引号内加;或\n\t作为结束标志;
第一个冒号后为输出域,第二个冒号后为输入域。域中格式为”操作符“(变量名),操作符前加修饰符=表示输出,无修饰符表示输入,变量为C代码中变量,多条输入或输出间用逗号隔开;
从输出域开始,对出现的变量从0开始依次编号,在汇编代码中以寄存器形式进行调用,上例中%0代表c,%1代表a,%2代表b。
第三个冒号后的参数在这里未用到,作用暂未了解;
操作符(大多数未实测):
操作符 | 含义 |
---|---|
r | 通用寄存器$R_0$-$R_{15}$ |
m | 一个有效内存地址 |
I | 数据处理指令中的立即数 |
X | 被修饰的操作符只能作为输出 |
a, b, c, d | 寄存器eax, ebx, ecx,edx |
q,r | 动态分配的寄存器 |
g | eax,ebx,ecx,edx或内存变量 |
数字”0” | 指定与第0个变量相同的约束 |
修饰符:
修饰符 | 说明 |
---|---|
无 | 被修饰的操作符是只读的 |
= | 被修饰的操作符只写 |
+ | 被修饰的操作符具有可读写的属性 |
& | 被修饰的操作符只能作为输出 |
执行程序
命令行或git执行下述命令:
1 | gcc -fno-builtin -m32 -masm=intel add.c -o add |
注:将add.c与add更换为相应的c文件名!
然后打开C文件同一目录下的同名exe文件,也可以:
1 | start add.exe |
注:将add.exe更换为相应的exe文件名!
- -fno-builtin 关闭编译器对汇编代码优化,与前文提到的volatile异曲同工,二者取其一即有效(未实测)。(可选)
- -m32 在64位环境下强制进行32位编译。(必选)
- -masm=intel 表示使用X86汇编。(X86必选,默认汇编语言不是X86)
- 打开exe文件后窗口可能立刻关闭或不便于记录输出结果,可以考虑在代码末端加入getch();或getchar();保持窗口打开,前者读取任意字符后结束进程,后者读取回车后结束进程。
运行效率
随机生成测试数据
使用排序来比较程序执行效率,先随机生成一些待排序数据。
1 |
|
冒泡排序横向对比
分别使用C语言与X86汇编编写冒泡排序函数,进行横向对比。
1 |
|
某次运行结果:
1 | Intel X86 bubbleSort.asm time: 6058 |
多次运行发现在该测试流程下,X86汇编的执行速度始终约为C语言执行速度的三倍。