汇编代码一般会与C函数的代码有数据交换,需要通过汇编代码来操作C代码中的一些变量数据,而C代码中的数据存放在内存还是寄存器,或者存放在哪个寄存器我们在写C代码的时候并不知道,所以汇编规定了一些形式来让编译器知道数据的交换情况,如下:
asm(
"mov %[result], %[value], ror #1"
: [result] "=r" (y)
: [value] "r" (x)
:
);
内联汇编通过冒号将内容分成了四个部分,内联汇编和C操作数之前的关联性体现在上面的input和out操作数上:
asm(
code /*汇编指令*/
: output operand list /*输出操作数列表*/
: input operand list /*输入操作数列表*/
: clobber list /*被改变资源列表*/
);
对前面的代码段分析,有四个组成部分:
1. 汇编指令
mov %[result], %[value], ror #1
2. 输出操作数列表,可选,每个输出数的符号名用方括号包围,后面跟一个约束串,然后再加上一个括号包围的C表达式,这个括号里的符号就是C语言代码中的变量。
[result] "=r" (y) /*result:符号名 "=r":约束串* (y):C表达式/
3. 输入操作数列表,可选,语法上与输入操作数列表一样。括号中的x是C语言代码中的变量。
[value] "r" (x)
4. 被改变资源列表,这里是空的,它主要是告诉编译器哪些资源发生了改变,需要去更新。
那么上面的"r"和"=r"是什么意思呢?请看下面两个图:
"=" 表示只写,通常用于所有输出操作数的属性
"+" 表示读写,只能被列为输出操作数的属性,否则编译会报错。
下表是寄存器类型的代表符号,r为通用寄存器,w是向量寄存器:
从图中可以看出,除了最常应用到的 "r" 属性(使用通用寄存器处理操作数),还有其他的很多特性,包括使用浮点寄存器 ("f"),或者使用立即数("I")等等,对于 arm 和 thumb 指令集也有较大的区别。
如果你曾经读过一些别人写的内联汇编代码就会发现与我们上面写的略有不同,是下面这种形式的:
asm(
"mov %0, %1, ror #1"
: "=r" (result)
: "r" (value)
:
);
实际上是同一种形式,只不过第一段汇编代码是从GCC 3.1版本开始才支持的,而在此之前一直是上面的形式。上述0%和1%分别表示第一个、第二个操作数。GCC的最新版本仍然支持上述语法,但明显,上述语法更容易出错,且难以维护:假设你写一个较长的内联汇编,然后需要在某个位置插入一个新的输出操作数,此时,之后的操作数都需要重新编号