在阅读本文之前,请先阅读gcc的相关文档,确保对如何在c中使用汇编语言有个基本的认识。 文档地址为: https://gcc./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. + 是在 = 的基础上,对编译器做的更严格的限制。 看个例子: #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: %d\n', inc1(1)); printf('inc2: %d\n', inc2(1)); } 这两个inc方法本应该都返回2,但实际却不是,我们执行看下:
由上可见,inc1方法返回的是一个莫名其妙的值(其实每次执行该程序,inc1返回的值都不同),这是因为inc1中指定的 constraint modifier 是 =,它表示在汇编代码里不会用到a原来的值,所以编译器可能会在add汇编指令执行之前,用到了a对应的寄存器,导致其原数据被覆盖,又可能它根本就没初始化a对应的寄存器为我们传入的值,总之,最终结果是错误的。 而在inc2方法中,我们指定的 constraint modifier 是 +,表示a原来的值在汇编代码中会被用到,所以编译器就不会改变a对应寄存器的值,所以最终结果是正确的。 我们再来看下两个方法对应的汇编代码,进一步确认下。 下面是inc1: $ gcc -O3 -S main.c && objdump --disassemble=inc1 a.out 0000000000001180 <inc1>: 1180: 83 c0 01 add $0x1,%eax 1183: c3 retq 下面是inc2:
通过对比我们可以发现,inc1方法里就根本没有初始化a对应的寄存器eax为a原来的值,这导致了在执行add操作时,a对应的寄存器中是一个随机值,所以最终结果是错误的。 通过上面的例子,我想你们应该已经明白了 = 和 + 的具体区别。 希望对你有所帮助。 完。 |
|