news 2026/4/29 11:10:33

C语言ASM汇编内嵌语法详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言ASM汇编内嵌语法详解

GCC 支持在C/C++代码中嵌入汇编代码,这些汇编代码被称作GCC Inline ASM——GCC内联汇编。这是一个非常有用的功能,有利于我们将一些C/C++语法无法表达的指令直接潜入C/C++代码中,另外也允许我们直接写 C/C++代码中使用汇编编写简洁高效的代码。

1.基本内联汇编

GCC中基本的内联汇编非常易懂,我们先来看两个简单的例子:

__asm__("movl %esp,%eax"); // 看起来很熟悉吧!

或者是

1

2

3

4

5

__asm__("

movl $1,%eax // SYS_exit

xor %ebx,%ebx

int $0x80

");

1

2

3

4

5

__asm__(

"movl $1,%eax\r\t" \

"xor %ebx,%ebx\r\t" \

"int $0x80" \

);

基本内联汇编的格式是

__asm__ __volatile__("Instruction List");

1、__asm__

__asm__是GCC关键字asm的宏定义:

#define __asm__ asm

__asm__或asm用来声明一个内联汇编表达式,所以任何一个内联汇编表达式都是以它开头的,是必不可少的。

2、Instruction List

Instruction List是汇编指令序列。它可以是空的,比如:__asm__ __volatile__(""); 或__asm__ ("");都是完全合法的内联汇编表达式,只不过这两条语句没有什么意义。但并非所有Instruction List为空的内联汇编表达式都是没有意义的,比如:__asm__ ("":::"memory"); 就非常有意义,它向GCC声明:“我对内存作了改动”,GCC在编译的时候,会将此因素考虑进去。

我们看一看下面这个例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

$ cat example1.c

int main(int __argc, char* __argv[])

{

int* __p = (int*)__argc;

(*__p) = 9999;

//__asm__("":::"memory");

if((*__p) == 9999)

return 5;

return (*__p);

}

在 这段代码中,那条内联汇编是被注释掉的。在这条内联汇编之前,内存指针__p所指向的内存被赋值为9999,随即在内联汇编之后,一条if语句判断__p 所指向的内存与9999是否相等。很明显,它们是相等的。GCC在优化编译的时候能够很聪明的发现这一点。我们使用下面的命令行对其进行编译:

$ gcc -O -S example1.c

选项-O表示优化编译,我们还可以指定优化等级,比如-O2表示优化等级为2;选项-S表示将C/C++源文件编译为汇编文件,文件名和C/C++文件一样,只不过扩展名由.c变为.s。

我们来查看一下被放在example1.s中的编译结果,我们这里仅仅列出了使用gcc 2.96在redhat 7.3上编译后的相关函数部分汇编代码。为了保持清晰性,无关的其它代码未被列出。

1

2

3

4

5

6

7

8

9

10

$cat example1.s

main:

pushl %ebp

movl %esp, %ebp

movl 8(%ebp), %eax # int* __p = (int*)__argc

movl $9999, (%eax) # (*__p) = 9999

movl $5, %eax # return 5

popl %ebp

ret

参 照一下C源码和编译出的汇编代码,我们会发现汇编代码中,没有if语句相关的代码,而是在赋值语句(*__p)=9999后直接return 5;这是因为GCC认为在(*__p)被赋值之后,在if语句之前没有任何改变(*__p)内容的操作,所以那条if语句的判断条件(*__p) == 9999肯定是为true的,所以GCC就不再生成相关代码,而是直接根据为true的条件生成return 5的汇编代码(GCC使用eax作为保存返回值的寄存器)。

我们现在将example1.c中内联汇编的注释去掉,重新编译,然后看一下相关的编译结果。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

$ gcc -O -S example1.c

$ cat example1.s

main:

pushl %ebp

movl %esp, %ebp

movl 8(%ebp), %eax # int* __p = (int*)__argc

movl $9999, (%eax) # (*__p) = 9999

#APP

# __asm__("":::"memory")

#NO_APP

cmpl $9999, (%eax) # (*__p) == 9999 ?

jne .L3 # false

movl $5, %eax # true, return 5

jmp .L2

.p2align 2

.L3:

movl (%eax), %eax

.L2:

popl %ebp

ret

由于内联汇编语句__asm__("":::"memory")向GCC声明,在此内联汇编语句出现的位置内存内容可能了改变,所以GCC在编译时就不能像刚才那样处理。这次,GCC老老实实的将if语句生成了汇编代码。

可能有人会质疑:为什么要使用__asm__("":::"memory")向GCC声明内存发生了变化?明明“Instruction List”是空的,没有任何对内存的操作,这样做只会增加GCC生成汇编代码的数量。

确 实,那条内联汇编语句没有对内存作任何操作,事实上它确实什么都没有做。但影响内存内容的不仅仅是你当前正在运行的程序。比如,如果你现在正在操作的内存 是一块内存映射,映射的内容是外围I/O设备寄存器。那么操作这块内存的就不仅仅是当前的程序,I/O设备也会去操作这块内存。既然两者都会去操作同一块 内存,那么任何一方在任何时候都不能对这块内存的内容想当然。所以当你使用高级语言C/C++写这类程序的时候,你必须让编译器也能够明白这一点,毕竟高 级语言最终要被编译为汇编代码。

你可能已经注意到了,这次输出的汇编结果中,有两个符号:#APP和#NO_APP,GCC将内联汇编语 句中"Instruction List"所列出的指令放在#APP和#NO_APP之间,由于__asm__("":::"memory")中“Instruction List”为空,所以#APP和#NO_APP中间也没有任何内容。但我们以后的例子会更加清楚的表现这一点。

关于为什么内联汇编__asm__("":::"memory")是一条声明内存改变的语句,我们后面会详细讨论。刚才我们花了大量的内容来讨论"Instruction List"为空是的情况,但在实际的编程中,"Instruction List"绝大多数情况下都不是空的。它可以有1条或任意多条汇编指令。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 11:10:28

给程序员的有机化学入门:用‘类与继承’的思维理解烃、醇、胺这些‘官能团’

给程序员的有机化学入门:用‘类与继承’的思维理解烃、醇、胺这些‘官能团’ 当你第一次翻开有机化学教材,可能会被各种复杂的分子结构和晦涩的命名规则吓到。但如果你有面向对象编程(OOP)的经验,其实你已经掌握了一套强大的思维工具来理解这…

作者头像 李华
网站建设 2026/4/29 11:07:51

Windows 10系统瘦身实战:用Win10BloatRemover重获纯净与性能

Windows 10系统瘦身实战:用Win10BloatRemover重获纯净与性能 【免费下载链接】Win10BloatRemover Configurable CLI tool to easily and aggressively debloat and tweak Windows 10 by removing preinstalled UWP apps, services and more. Originally based on th…

作者头像 李华
网站建设 2026/4/29 11:07:21

别再死磕格式了!Paperxie 一键搞定论文排版,让你少熬 3 个通宵

paperxie-免费查重复率aigc检测/开题报告/毕业论文/智能排版/文献综述/期刊论文https://www.paperxie.cn/format/typesettinghttps://www.paperxie.cn/format/typesetting 写毕业论文最磨人的是什么?不是写初稿,不是改降重,而是对着学校几十页…

作者头像 李华