09. C语言内嵌汇编代码

C语言函数内可以自定义一段汇编代码,在GCC编译器中使用 asm 或 asm 关键词定义一段汇编代码,并可选添加volatile关键字,表示不要让编译器优化这段汇编代码。

内嵌汇编代码格式如下:

cpp 复制代码
__asm__
(
    "汇编代码"
    :输出描述
    :输入描述
    :修改描述
);

汇编代码部分

汇编代码部分是一个字符串,嵌入的汇编代码使用此字符串存储,多条汇编代码之间使用;符号隔开,字符串可以换行存储,换行存储方式如下:

cpp 复制代码
__asm__
(
    "mov eax,ecx; \
    add eax,ebx;"
);

或者:

cpp 复制代码
__asm__
(
    "mov eax,ecx;"
    "add eax,ebx;"
);

注:

1.汇编代码中16进制数据只能使用0x前缀,不能使用h后缀。

2.指定内存数据长度时,使用 word prt 关键词,不能省略 prt。

3.内嵌汇编代码默认使用AT&T语法,若使用inter语法则需要在编译时添加如下参数:-masm=intel,本文使用inter汇编语法。

输出描述部分

若汇编代码执行完毕后需要将寄存器、内存单元中的数据保存在C语言代码定义的局部变量中,则需要在输出描述部分定义寄存器或内存单元与局部变量的绑定关系,绑定代码格式如下:

cpp 复制代码
#include <stdio.h>
int main()
{
    int i;

    __asm__
    (
        "mov eax,1; \
        mov ebx,2; \
        add eax,ebx;"
        
        :"=a"(i)          //输出描述部分,"=a"表示输出eax寄存器,(i)表示使用变量i接收输出数据
    );
    
    printf("%d\n", i);
    return 0;
}

输出描述部分使用简写代码表示要输出的寄存器或内存单元,常用代码如下:

a,表示ax系寄存器

b,表示bx系寄存器

c,表示cx系寄存器

d,表示dx系寄存器

S,表示si系寄存器

D,表示di系寄存器

r,表示自动分配的寄存器

m,表示自动分配的内存单元

g,表示自动分配的寄存器或内存单元

绑定的局部变量可以使用指针指定。

cpp 复制代码
#include <stdio.h>
int main()
{
    int i;
    int *p1 = &i;
    
    __asm__
    (
        "mov eax,1; \
        mov ebx,2; \
        add eax,ebx;"
        
        :"=a"(*p1)
    );
    
    printf("%d\n", i);
    return 0;
}

输入描述部分

输入描述用于将局部变量与指定的寄存器或内存单元绑定,绑定的变量会首先复制到对应的寄存器或内存单元,然后再执行汇编代码。

cpp 复制代码
#include <stdio.h>
int main()
{
    int i1, i2, i3;
    
    printf("输入两个整数\n");
    scanf("%d%d", &i1, &i2);
    
    __asm__
    (
        "add eax,ebx;"
        
        :"=a"(i3)            //输出描述部分,eax写入i3
        
        :"a"(i1), "b"(i2)    //输入描述部分,i1写入eax,i2写入ebx
    );
    
    printf("两数相加结果为:%d\n", i3);
    return 0;
}

修改描述部分

修改描述用于告知编译器哪些寄存器、内存单元被汇编代码修改过,让编译器在编译代码时对这些寄存器或内存单元进行保护,当然我们也可以手动进行保护,在使用寄存器之前首先将其入栈存储,在汇编代码末尾处还原寄存器。

修改描述注意事项:

1.若被修改的寄存器在输出描述、输入描述中记录过,则无需在修改描述中重复指定,编译器会自动处理。

2.若修改了内存单元,则应该在此部分定义"memory"。

3.若修改了标志寄存器,则应该在此部分定义"c"。

cpp 复制代码
#include <stdio.h>
int main()
{
	int i1, i2, i3;
	
	printf("输入两个整数\n");
	scanf("%d%d", &i1, &i2);
	
	__asm__
	(
		"add eax,ebx; \
		mov edx,3; \
		imul ecx,edx"        //ecx与edx相乘仅做示例,没有实际作用
		
		:"=a"(i3)            //输出描述部分,eax写入i3
		
		:"a"(i1), "b"(i2)    //输入描述部分,i1写入eax,i2写入ebx
		
		:"ecx", "edx"        //修改描述部分,告知编译器ecx、edx被修改过,并且没有在输入输出描述中记录
	);
	
	printf("两数相加结果为:%d\n", i3);
	return 0;
}

编译器自动分配寄存器、内存单元

在汇编代码中存储数据时可以让编译器自动分配寄存器或内存,汇编代码使用"%数字"的方式调用编译器自动分配的寄存器或内存,比如:%0、%1、%2,这些名称称为占位符,占位符可以不按顺序定义。

占位符可以与输入输出描述中的局部变量绑定,绑定顺序为局部变量在输入输出描述中出现的顺序,%0绑定第一个变量、%1第二个、%2第三个。

cpp 复制代码
#include <stdio.h>
int main()
{
    int i1, i2, i3;
    
    printf("输入两个整数\n");
    scanf("%d%d", &i1, &i2);
    
    __asm__
    (
        "add %1,%2; \
        mov %0,%1"
        
        :"=m"(i3)            //输出描述部分,%0写入i3,这里使用m,表示让编译器自动分配内存单元存储绑定的i3
        
        :"r"(i1), "r"(i2)    //输入描述部分,i1写入%1,i2写入%2,这里使用r,表示让编译器自动分配寄存器存储绑定的i1、i2
    );
    
    printf("两数相加结果为:%d\n", i3);
    return 0;
}

上面汇编代码中三个变量出现顺序是i3、i1、i2,%0绑定i3,%1绑定i1,%2绑定i2。

使用全局变量

在汇编代码中调用全局变量无需进行任何设置,直接使用变量名即可,编译器会自动转换为对应的内存地址。

cpp 复制代码
#include <stdio.h>
int i1, i2;
int main()
{
    printf("输入两个整数\n");
    scanf("%d%d", &i1, &i2);
    
    __asm__
    (
        "push rax; \
        mov eax,i1; \
        add eax,i2; \
        mov i1,eax; \
        pop rax;"
    );
    
    printf("两数相加结果为:%d\n", i1);
    return 0;
}
相关推荐
子朔不言14 分钟前
[ARM-2D 专题]6.脏矩形定义的宏使用技巧和分析
c语言·arm开发·arm2d·显控开发-新龙微
BT-BOX1 小时前
STM32仿真proteus位带操作和keil增加头文件C文件
c语言·stm32·proteus
醉颜凉1 小时前
【NOIP提高组】潜伏者
java·c语言·开发语言·c++·算法
五味香4 小时前
Linux学习,ip 命令
linux·服务器·c语言·开发语言·git·学习·tcp/ip
lb36363636365 小时前
整数储存形式(c基础)
c语言·开发语言
浪里个浪的10245 小时前
【C语言】从3x5矩阵计算前三行平均值并扩展到4x5矩阵
c语言·开发语言·矩阵
<但凡.5 小时前
编程之路,从0开始:知识补充篇
c语言·数据结构·算法
f狐0狸x6 小时前
【数据结构副本篇】顺序表 链表OJ
c语言·数据结构·算法·链表
CoderBob6 小时前
【EmbeddedGUI】脏矩阵设计说明
c语言·单片机
浪里个浪的10246 小时前
【C语言】计算3x3矩阵每行的最大值并存入第四列
c语言·开发语言·矩阵