c、数组与汇编
上次的“c调用汇编”使用的clang编译器,而且使用的是mac 64位系统。当时感觉写的汇编怪怪的,
今天闲来无事,就在w7 32位系统下把c文件汇编后,确实与mac后的差异很大。
可不仅仅是寄存器eax与rax的区别。我想说的是函数参数传递的不同。mac下clang编译后
函数的参数先保存在寄存器中(以一定的规则保存),然后在函数中压入栈里,
以待后用。例如上篇例子,红色部分:
.global _decToBin _decToBin: pushq %rbp movq %rsp,%rbp movq %rdi,-8(%rbp) #第一个参数,保存在rdi中 movq %rsi,-16(%rbp) #第二个参数,保存在rsi中 movq -8(%rbp),%rax movq -16(%rbp),%rbx movq $63,%rcx ...... popq %rbp ret
而我在w7下使用cygwin安装的gcc编译test.c文件:
test.c:
int hello(int a,int b,int c,int d) { return b; }
test.s:
.file "test.c" .text .globl _hello .def _hello; .scl 2; .type 32; .endef _hello: pushl %ebp movl %esp, %ebp movl 12(%ebp), %eax #说明参数是函数在使用其值之前就已经压入栈中 popl %ebp ret
这说明clang与gcc使用了两种不同的规则(网上有很多介绍函数值传递的不同规则的,我就不介绍了)。
所以不同的平台不同的编译器要不同的对待。以上算是上次的不足补充吧。
下面来看看数组:
test.c例子:
1 void hello1() 2 { 3 int a[3]={1,2,3}; 4 int b=a[1]; 5 } 6 void hello2() 7 { 8 int a[3]={1,2,3}; 9 int b=*(a+1); 10 } 11 void hello3() 12 { 13 int a[3]={1,2,3}; 14 int b=1[a]; //这也对? 15 }
如果看的够仔细的话,三个函数没什么不同就是对数组a[1]的不同(当然函数名除外).
gcc -S test.c 后:
1 .file "test.c" 2 .data 3 .align 4 4 LC0: 5 .long 1 6 .long 2 7 .long 3 8 .text 9 .globl _hello1 10 .def _hello1; .scl 2; .type 32; .endef 11 _hello1: 12 pushl %ebp 13 movl %esp, %ebp 14 pushl %edi 15 pushl %esi 16 pushl %ebx 17 subl $16, %esp 18 leal -28(%ebp), %edx 19 movl $LC0, %ebx 20 movl $3, %eax 21 movl %edx, %edi 22 movl %ebx, %esi 23 movl %eax, %ecx 24 rep movsl 25 movl -24(%ebp), %eax 26 movl %eax, -16(%ebp) 27 addl $16, %esp 28 popl %ebx 29 popl %esi 30 popl %edi 31 popl %ebp 32 ret 33 .globl _hello2 34 .def _hello2; .scl 2; .type 32; .endef 35 _hello2: 36 pushl %ebp 37 movl %esp, %ebp 38 pushl %edi 39 pushl %esi 40 pushl %ebx 41 subl $16, %esp 42 leal -28(%ebp), %edx 43 movl $LC0, %ebx 44 movl $3, %eax 45 movl %edx, %edi 46 movl %ebx, %esi 47 movl %eax, %ecx 48 rep movsl 49 leal -28(%ebp), %eax 50 movl 4(%eax), %eax 51 movl %eax, -16(%ebp) 52 addl $16, %esp 53 popl %ebx 54 popl %esi 55 popl %edi 56 popl %ebp 57 ret 58 .globl _hello3 59 .def _hello3; .scl 2; .type 32; .endef 60 _hello3: 61 pushl %ebp 62 movl %esp, %ebp 63 pushl %edi 64 pushl %esi 65 pushl %ebx 66 subl $16, %esp 67 leal -28(%ebp), %edx 68 movl $LC0, %ebx 69 movl $3, %eax 70 movl %edx, %edi 71 movl %ebx, %esi 72 movl %eax, %ecx 73 rep movsl 74 movl -24(%ebp), %eax 75 movl %eax, -16(%ebp) 76 addl $16, %esp 77 popl %ebx 78 popl %esi 79 popl %edi 80 popl %ebp 81 ret
只要看红色的行,我们可以看到25-27行与74-76行一样,说明hello1与hello3没什么不同,
效率一样。而49-52行比他们多了一行,所以*(a+1)比a[1]和1[a]要低一点。
但是我们看下面的例子。
test1.c与test2.c:
View Code
//1-------------- #include <stdlib.h> void hello() { int *a=(int*)malloc(sizeof(int)*3); int b=*(a+1); free(a); } //2-------------- #include <stdlib.h> void hello() { int *a=(int*)malloc(sizeof(int)*3); int b=a[1]; free(a); }
汇编后完全一样:
View Code
1 .file "main.c" 2 .text 3 .globl _hello 4 .def _hello; .scl 2; .type 32; .endef 5 _hello: 6 pushl %ebp 7 movl %esp, %ebp 8 subl $40, %esp 9 movl $12, (%esp) 10 call _malloc 11 movl %eax, -12(%ebp) 12 movl -12(%ebp), %eax 13 movl 4(%eax), %eax 14 movl %eax, -16(%ebp) 15 leave 16 ret 17 .def _malloc; .scl 2; .type 32; .endef
所以在堆中使用*(a+n)与a[n]没什么不同,只用在栈中才会有所不同。
学习汇编不是必要,但是它可以让我们知道效率。