c语言内嵌汇编代码之constraint modifier中 = 和 + 的区别

2019-10-14 16:29:54 浏览数 (1)

在阅读本文之前,请先阅读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对应的寄存器中是一个随机值,所以最终结果是错误的。

通过上面的例子,我想你们应该已经明白了 = 和 的具体区别。

希望对你有所帮助。

0 人点赞