__asm__ ("addl %1, %0nt"
: "=r"(__out)
: "%r" (__in2), "0" (__in1));
修饰符 Input/Output 意义 = O 表示此Output操作表达式是Write-Only的 + O 表示此Output操作表达式是Read-Write的 & O 表示此Output操作表达式独占为其指定的寄存器 % I 表示此Input操作表达式中的C/C++表达式可以和下一个Input操作表达式中的C/C++表达式互换
4. 占位符
什么叫占位符?我们看一看下面这个例子:
__asm__ ("addl %1, %0nt"
: "=a"(__out)
: "m" (__in1), "a" (__in2));
这 个例子中的%0和%1就是占位符。每一个占位符对应一个Input/Output操作表达式。我们在之前已经提到,GCC规定一个内联汇编语句最多可以有 10个Input/Output操作表达式,然后按照它们被列出的顺序依次赋予编号0到9。对于占位符中的数字而言,和这些编号是对应的。
由于占位符前面使用一个百分号(%),为了区别占位符和寄存器,GCC规定在带有C/C++表达式的内联汇编中,"Instruction List"中直接写出的寄存器前必须使用两个百分号(%%)。
GCC 对其进行编译的时候,会将每一个占位符替换为对应的Input/Output操作表达式所指定的寄存器/内存地址/立即数。比如在上例中,占位符%0对应 Output操作表达式"=a"(__out),而"=a"(__out)指定的寄存器为%eax,所以把占位符%0替换为%eax,占位符%1对应 Input操作表达式"m"(__in1),而"m"(__in1)被指定为内存操作,所以把占位符%1替换为变量__in1的内存地址。
也许有人认为,在上面这个例子中,完全可以不使用%0,而是直接写%%eax,就像这样:
__asm__ ("addl %1, %%eaxnt"
: "=a"(__out)
: "m" (__in1), "a" (__in2));
和 上面使用占位符%0没有什么不同,那么使用占位符%0就没有什么意义。确实,两者生成的代码完全相同,但这并不意味着这种情况下占位符没有意义。因为如果 不使用占位符,那么当有一天你想把变量__out的寄存器约束由a改为b时,那么你也必须将addl指令中的%%eax改为%%ebx,也就是说你需要同 时修改两个地方,而如果你使用占位符,你只需要修改一次就够了。另外,如果你不使用占位符,将不利于代码的清晰性。在上例中,如果你使用占位符,那么你一 眼就可以得知,addl指令的第二个操作数内容最终会输出到变量__out中;否则,如果你不用占位符,而是直接将addl指令的第2个操作数写为%% eax,那么你需要考虑一下才知道它最终需要输出到变量__out中。这是占位符最粗浅的意义。毕竟在这种情况下,你完全可以不用。
但对于这些情况来说,不用占位符就完全不行了:
首 先,我们看一看上例中的第1个Input操作表达式"m"(__in1),它被GCC替换之后,表现为addl address_of_in1, %%eax,__in1的地址是什么?编译时才知道。所以我们完全无法直接在指令中去写出__in1的地址,这时使用占位符,交给GCC在编译时进行替 代,就可以解决这个问题。所以这种情况下,我们必须使用占位符。










