C(GNU) 内嵌汇编

内联汇编:在C/C++代码中嵌入汇编代码。

汇编的用武之地:

  • 效率依旧比C高。
  • 有特殊的指令必须用汇编,在C中没有等价的语法。

 

(1) 内嵌汇编的格式

GNU下,在高级语言中嵌汇编语言用关键字asm来实现,简单的格式如下:

asm( “assembly code” );

  • asm用来标识汇编代码段。括号内的汇编代码必须在引号之内。
  • 如果asm模块内有多条语句,若每条语句独占一行则需要将每条语句都用双引号引起来。并且每条语句的末尾需要有换行。 

C中嵌汇编的例子,记录C中嵌汇编的格式:

1  #include <stdio.h>

2

3  int main(void)

4  {

5         //AT&T asm systemcall: exit

6         asm(    "movl $1, %eax\n\t"

7                 "movl $0,%ebx\n\t"

8                 "int $0x80");

 9

 10        //Print string on screen

 11        printf("Hello ""world"" C\n");

 12

 13        return 0;

 14  }
  • 68行是用GNU内敛汇编的格式编写的汇编代码。这些汇编代码表示系统调用exit函数,让程序退出。
  • asm括号内有多行汇编代码时,没行代码都需要括起来且需要以\n\t结束。如果不加\n\t的话下一行的首字母会被连接到上一行的末尾(这个是双引号的用法)。关于加\n\t的机制,可能在windows之上不一样。
  •  如果,内敛汇编成功,那么编译并执行此C语言程序是不会输出字符串的。

(2)内敛汇编代码访问C的全局变量

C中内嵌的汇编代码能够处理在C中的变量,可惜只能处理全局变量(已验证)。以下笔记示例:

1  #include <stdio.h>

  2

  3  int a;

  4  int b;

  5  int c;

  6

  7  int main(void)

  8  {

 9         a = b = 1;

 10        c =0;

 11        //AT&T asm system call: exit

 12        asm(    "movl a,%eax\n\t"

 13                 "movl b, %ebx\n\t"

 14                 "addl %eax,%ebx\n\t"

 15                 "movl %ebx, c");

 16

 17        //Print string on screen

 18        printf("Result is %d\n", c);

 19

 20        return 0;

 21  }
  • 12行至15行代码是内敛的汇编代码。用来处理全局变量a, b, c。在内敛汇编中处理C中的全局变量按照汇编的规则直接引用变量的名称即可。
  • 内嵌汇编代码的C程序编译跟编译纯C代码一样。结果输出C的值为2

 

(3)内敛汇编扩展

使用以上的内敛汇编代码有一定的局限性,比如在内敛汇编代码中只能访问C的全局变量。而在扩展内敛汇编代码内就能够让内敛汇编代码访问C的任何变量。GNU的扩展内敛汇编笔记如下。

 

[1]扩展内敛汇编格式

asm (assembly code

        :output locations

      :input operands 

        :change dregisters);

  • assembly code:  跟前面笔记的内敛汇编代码具一样的格式。
  • output locations指示汇编指令的运算结果要输出到哪些C操作数中,C操作数应该是左值表达式
  • input operands第三部分指示汇编指令需要从哪些C操作数获得输入
  • changed registers第四部分是在汇编指令中被修改过的寄存器列表,指示编译器哪些寄存器的值在执行这条asm指令后会改变.

对于后三块,没有的模块可以直接省略。非最后一部分省略时冒号不可被省略。

 

output locationsinput operands具有的格式:

constraint1(variable1)constraint2(variable2)…

  • constraint定义variable将被置于何处。[可查看《Professional Assembly language page402]
  • variable是在C中定义的变量。

 

[2]使用扩展内敛汇编

还是针对于以上的内敛汇编代码,将全局变量改为局部变量,用扩展内敛汇编去访问C中的变量。

 

<1>寄存器

1  #include <stdio.h>

  2

  3

  4  int main(void)

  5  {

 6         int a, b, c;

  7

 8         a = b = 1;

 9         c =0;

 10        //AT&T asm system call: exit

 11        asm(    "movl $0,%%eax\n\t"

 12                 "addl %%ebx,%%ecx\n\t"

 13                 "addl %%ecx,%%eax\n\t"

 14                 : "=a"(c)

 15                : "b"(a),"c"(b)

 16        );

 17

 18        //Print string on screen

 19        printf("Result is %d\n", c);

 20

 21        return 0;

 22  }
  • 定义局部变量a, b, c供扩展内敛汇编代码访问。
  • 1116行是扩展内敛汇编代码。针对之前提到的笔记对应解释这段内敛汇编代码。
  • 14对应output locations”a”表示eax寄存器,”c”表示程序中定义定义的变量c。在output locations限制内存处要加“=”。即经此段内敛内敛代码处理后,寄存器eax的值将赋值给变量c
  • 15对应input operands”b””c”分别表示ebxecx寄存器。”a””b”分表代表程序中的变量bc。此语句表面,将程序中的变量值bc分别放在寄存器ebxecx中。
  • 11行到13行的汇编代码依次表示初始化eax为0,将ebx与ecx内的值相加后放在ecx中,将ecx和eax内的值相加后放在eax中。

 

用寄存器来作为C变量的存储地,再经汇编代码处理。这个过程其实就是将变量的值拷贝到各寄存器中,然后操作各寄存器,再将作为输出的变量重新赋值。不过为什么在这种情况下要在寄存器前面加两个%应该与%本身功能相关,点到为止,遇到再议。

 

<2>占位符

当需要内敛汇编代码处理的代码数量较多时,可能会有不知道用哪个寄存器来保存输入变量才比较,恐怕有心有余而力不足的赶脚。使用占位符可解决此烦恼。

 

占位符用除寄存器外的其它constraint[可查看《Professional Assembly languagepage402]来表示。如:

asm(   "addl %1, %2\n\t"

        "addl %2, %0\n\t"

        : "=r"(c)

       : "r"(a), "r"(b)

);
  • r只能表示任何可用的通用寄存器。
  • %0代表存c变量的地方。
  • %1代表存a变量的地方。
  • %2代表存b变量的地方。
  • 故而汇编代码的操作就是操作%0, %1, %2了,能得到相同的效果。

 

使用占位符的情况还可以如此:

asm ( “addl  %[value1],  %[value2]”

      : [value2]  “=r” (data2)

       :[value1]  “r”(data1));

 

<3>内存空间

内存空间的限制符为m。在使用内存空间时,需要用一个寄存器来作为中间存储。

C(GNU) 内嵌汇编_第1张图片

 

还有其它形式的扩展内敛汇编,形体都差不多。在实际编程中可根据需求和爱好进行选择。

 

(4)内敛汇编的标识符

[1]asm volatile(“assembly code”);

asm之后加了volatile之后,表明括号内的汇编代码不被优化。这些是volatile关键字的作用,道理深着。

 

[2]__asm_

GNU下可使用asm来表示内敛的汇编代码。如果是在ANSC C中编写内敛汇编代码,需要用__asm_来替换asm。同时volatile也需要换成__volatile_

 

(5)内敛汇编用途总结

C/C++中使用内敛汇编的很多形式为宏。即将内敛汇编定义成宏供C/C++代码调用。将内敛汇编定义成宏的形式更加方便。定义宏的方式跟在C中定义C宏的方法一致,只是宏的形式跟以上笔记的内敛汇编的形式一致。

 

CNote Over。

你可能感兴趣的:(C(GNU) 内嵌汇编)