在阅读本文之前,请先阅读gcc的相关文档,确保对如何在c中使用汇编语言有个基本的认识。
文档地址为:
https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C
1. = 和 只能用于 output operands,不能用于 input operands。
2. output operands 的 constraint 字符串必须以 = 或 开始。
3. = 和 都表示对应的 output operand 有写操作。
4. = 表示的可写,是告诉编译器在执行这条asm语句时,该 output operand 原来的值不会被用到,所以它占用的寄存器或内存在写操作发生之前可以被随便使用。
5. 表示的可写,是告诉编译器在执行这条asm语句时,该 output operand 原来的值会被用到,所以它占用的寄存器或内存不能被随便使用,否则可能会导致在该 output operand 被使用时,原数据被覆盖了。
6. 是在 = 的基础上,对编译器做的更严格的限制。
看个例子:
代码语言:javascript复制#include <stdio.h>
int inc1(int a) {
asm("add $1, %0" : "=r" (a));
return a;
}
int inc2(int a) {
asm("add $1, %0" : " r" (a));
return a;
}
int main(int argc, char *argv[]) {
printf("inc1: %dn", inc1(1));
printf("inc2: %dn", inc2(1));
}
这两个inc方法本应该都返回2,但实际却不是,我们执行看下:
代码语言:javascript复制$ gcc -O3 main.c && ./a.out
inc1: 1998320153
inc2: 2
由上可见,inc1方法返回的是一个莫名其妙的值(其实每次执行该程序,inc1返回的值都不同),这是因为inc1中指定的 constraint modifier 是 =,它表示在汇编代码里不会用到a原来的值,所以编译器可能会在add汇编指令执行之前,用到了a对应的寄存器,导致其原数据被覆盖,又可能它根本就没初始化a对应的寄存器为我们传入的值,总之,最终结果是错误的。
而在inc2方法中,我们指定的 constraint modifier 是 ,表示a原来的值在汇编代码中会被用到,所以编译器就不会改变a对应寄存器的值,所以最终结果是正确的。
我们再来看下两个方法对应的汇编代码,进一步确认下。
下面是inc1:
代码语言:javascript复制$ gcc -O3 -S main.c && objdump --disassemble=inc1 a.out
0000000000001180 <inc1>:
1180: 83 c0 01 add $0x1,�x
1183: c3 retq
下面是inc2:
代码语言:javascript复制$ gcc -O3 -S main.c && objdump --disassemble=inc2 a.out
0000000000001190 <inc2>:
1190: 89 f8 mov �i,�x
1192: 83 c0 01 add $0x1,�x
1195: c3 retq
通过对比我们可以发现,inc1方法里就根本没有初始化a对应的寄存器eax为a原来的值,这导致了在执行add操作时,a对应的寄存器中是一个随机值,所以最终结果是错误的。
通过上面的例子,我想你们应该已经明白了 = 和 的具体区别。
希望对你有所帮助。