用C写程序比直接用汇编写程序更简洁,可读性更好,但效率可能不如汇编程序,因为C程序毕竟要经由编译器生成汇编代码,尽管现代编译器的优化已经做得很好了,但还是不如手写的汇编代码。另外,有些平台相关的指令必须手写,在C语言中没有等价的语法,因为C语言的语法和概念是对各种平台的抽象,而各种平台特有的一些东西就不会在C语言中出现了,例如x86是端口I/O,而C语言就没有这个概念,所以in/out指令必须用汇编来写。

C语言简洁易读,容易组织规模较大的代码,而汇编效率高,而且写一些特殊指令必须用汇编,为了把这两方面的好处都占全了,gcc提供了一种扩展语法可以在C代码中使用内联汇编(Inline Assembly)。最简单的格式是

__asm__("assembly code");

例如

__asm__("nop");

nop 这条指令什么都不做,只是让CPU空转一个指令执行周期。如果需要执行多条汇编指令,则应该用\n\t将各条指令分隔开,例如:

__asm__("movl $1, %eax\n\t"
"movl$4, %ebx\n\t"
"int$0x80");

通常 C 代码中的内联汇编需要和C的变量建立关联,需要用到完整的内联汇编格式:

__asm__(assembler template
:output operands /*optional */
:input operands /*optional */
:list of clobbered registers /*optional */
);

这种格式由四部分组成,第一部分是汇编指令,和上面的例子一样,第二部分和第三部分是约束条件,第二部分指示汇编指令的运算结果要输出到哪些C操作数中,C操作数应该是左值表达式,第三部分指示汇编指令需要从哪些C操作数获得输入,第四部分是在汇编指令中被修改过的寄存器列表,指示编译器哪些寄存器的值在执行这条__asm__语句时会改变。后三个部分都是可选的,如果有就填写,没有就空着只写个:号。例如:

#include <stdio.h>

int main(void)
{
int a = 10, b; __asm__("movl%1, %%eax\n\t"
"movl%%eax, %0\n\t"
:"=r"(b) /* output */
:"r"(a) /* input */
:"%eax" /* clobbered register */
);
printf("Result:%d, %d\n", a, b);
return0;
}

这个程序将变量a的值赋给b。"r"(a)指示编译器分配一个寄存器保存变量a的值,作为汇编指令的输入,也就是指令中的%1(按照约束条件的顺序,b对应%0,a对应1%),至于%1究竟代表哪个寄存器则由编译器自己决定。汇编指令首先把%1所代表的寄存器的值传给eax(为了和%1这种占位符区分,eax前面要求加两个%号),然后把eax的值再传给%0所代表的寄存器。"=r"(b)就表示把%0所代表的寄存器的值输出给变量b。在执行这两条指令的过程中,寄存器eax的值被改变了,所以把"%eax"写在第四部分,告诉编译器在执行这条__asm__语句时eax要被改写,所以在此期间不要用eax保存其它值。

上面的程序完成将变量a的值赋予变量b,有几点需要说明:

1、变量b是输出操作数,通过%0来引用,而变量a是输入操作数,通过%1来引用。

2、输入操作数和输出操作数都使用r进行约束,表示将变量a和变量b存储在寄存器中。输入约束和输出约束的不同点在于输出约束多一个约束修饰符'='。

3、在内联汇编语句中使用寄存器eax时,寄存器名前应该加两个'%',即%%eax。内联汇编中使用%0、%1等来标识变量,任何只带一个'%'的标识符都看成是操作数,而不是寄存器。

内联汇编语句的最后一个部分告诉GCC它将改变寄存器eax中的值,GCC在处理时不应使用该寄存器来存储任何其它的值。

4、由于变量b被指定成输出操作数,当内联汇编语句执行完毕后,它所保存的值将被更新。

5、在内联汇编中用到的操作数从输出部的第一个约束开始编号,序号从0开始,每个约束记数一次,指令部要引用这些操作数时,只需在序号前加上'%'作为前缀就可以了。

我们看一下这个程序的反汇编结果:

       __asm__("movl %1, %%eax\n\t"
80483dc: 8b 55 f8 mov -0x8(%ebp),%edx
80483df: 89 d0 mov %edx,%eax
80483e1: 89 c2 mov %eax,%edx
80483e3: 89 55 f4 mov %edx,-0xc(%ebp)
"movl %%eax, %0\n\t"
:"=r"(b) /* output */
:"r"(a) /* input */
:"%eax" /* clobbered register */
);

可见%0和%1都代表edx寄存器,首先把变量a(位于ebp-8的位置)的值传给edx然后执行内联汇编的两条指令,然后把edx的值传给b(位于ebp-12的位置)。

有关内联汇编,通常情况下只需要了解这些就足够了。

C语言的本质(32)——C语言与汇编之C语言内联汇编的更多相关文章

  1. C语言中递归什么时候能够省略return引发的思考:通过内联汇编解读C语言函数return的本质

    事情的经过是这种,博主在用C写一个简单的业务时使用递归,因为粗心而忘了写return.结果发现返回的结果依旧是正确的.经过半小时的反汇编调试.证明了我的猜想,如今在博客里分享.也是对C语言编译原理的一 ...

  2. 通过调用C语言的库函数与在C代码中使用内联汇编两种方式来使用同一个系统调用来分析系统调用的工作机制

    通过调用C语言的库函数与在C代码中使用内联汇编两种方式来使用同一个系统调用来分析系统调用的工作机制 前言说明 本篇为网易云课堂Linux内核分析课程的第四周作业,我将通过调用C语言的库函数与在C代码中 ...

  3. Ok6410裸机驱动学习(三)C语言内嵌汇编

    1.C语言内嵌汇编使用方法 C内嵌汇编以关键字”_asm_或asm开始,下辖4个部分,各部分之间用“:”分开,第一部分是必须写的,后面3个部分可以省略,但是分号:不能省略 优化后的代码 2.汇编程序框 ...

  4. C语言的本质(3)——整数的本质与运算

    C语言的本质(3)--整数的本质与运算 计算机存储的最小单位是字节(Byte),一个字节通常是8个bit.C语言规定char型占一个字节的存储空间.如果这8个bit按无符号整数来解释,则取值范围是0~ ...

  5. C语言的本质(28)——C语言与汇编之用汇编写一个Helloword

    为了更加深入理解C语言的本质,我们需要学习一些汇编相关的知识.作为最基本的编程语言之一,汇编语言虽然应用的范围不算很广,但是非常重要.因为它能够完成许多其它语言所无法完成的功能.就拿 Linux 内核 ...

  6. C语言的本质(15)——C语言的函数接口入门

    C语言的本质(15)--C语言的函数接口 函数的调用者和其实现者之间存在一个协议,在调用函数之前,调用者要为实现者提供某些条件,在函数返回时,实现者完成调用者需要的功能. 函数接口通过函数名,参数和返 ...

  7. C语言的本质(7)——C语言运算符大全

    C语言的本质(7)--C语言运算符大全 C语言的结合方向 C语言中各运算符的结合性分为两种,即左结合性(自左至右)和右结合性(自右至左).例如算术运算符的结合性是自左至右,即先左后右.如有表达式 x- ...

  8. C语言的本质(4)——浮点数的本质与运算

    C语言的本质(4)--浮点数的本质与运算 C语言规定了3种浮点数,float型.double型和long double型,其中float型占4个字节,double型占8个字节,longdouble型长 ...

  9. C语言的本质(33)——GCC编译器入门

    GCC(GNU CompilerCollection,GNU编译器套装),是由 GNU 开发的编程语言编译器.它是以GPL许可证所发行的自由软件,也是 GNU计划的关键部分.GCC原本作为GNU操作系 ...

随机推荐

  1. 记一次 java程序优化

    优化原因 环境中部署两个程序: web应用 tomcat   10G(webservice服务端,前端web服务) java应用               5G(webservice客户端,sock ...

  2. Find Successor & Predecessor in BST

    First, we use recursive way. Successor public class Solution { public TreeNode inorderSuccessor(Tree ...

  3. nyoj 104 最大和 (二维最大字串和)

    描述 给定一个由整数组成二维矩阵(r*c),现在需要找出它的一个子矩阵,使得这个子矩阵内的所有元素之和最大,并把这个子矩阵称为最大子矩阵. 例子: - - - - - - - 其最大子矩阵为: - - ...

  4. JQuery 动画之 广告

    html页面: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head&g ...

  5. Linux环境 Mysql新建用户和数据库并授权

    测试环境:linux 和Mysql 5.5.35 一.新建用户 //登录Mysql@>mysql -u root -p@>密码//创建用户mysql> insert into mys ...

  6. ASP.NET中时间的绑定和格式化

    1.Eval和Bind的区别  绑定表达式  <%# Eval("字段名") %>  <%# Bind("字段名") %> 区别 1.e ...

  7. 【Struts2】新建一个Struts2工程,初步体验MVC

    实现目标 地址栏输入http://localhost:88/Struts2HelloWorld/helloworld.jsp 输入用户名,交由http://localhost:88/Struts2He ...

  8. [Hapi.js] Managing State with Cookies

    hapi has built-in support for parsing cookies from a request headers, and writing cookies to a respo ...

  9. IOS 设备信息读取

    let infoDictionary = NSBundle.mainBundle().infoDictionary let appDisplayName: AnyObject? = infoDicti ...

  10. rhel5.8-LAMP环境搭建

    一.LAMP安装前的准备   安装环境:rhel5.8  zabbix-2.4.5  php-5.6.8   MySQL5.6.23  libpng-1.5.9 zlib-1.2.7 (apr,apr ...