精通汇编和c的大佬来回答一下:从汇编的角度来理解一下这个函数传参为什么传的是值

最近写函数突然联想到很多问题,比如我们一般说函数传参传的是值不是地址,想要通过函数来修改一个参数的值我们得用指针,然后我想从汇编的角度来理解,就用了非常经典的swap然后反汇编。但是感觉汇编代码不管是传值还是传指针感觉差不多啊。大佬们能不能解释一下,明明函数里的堆栈使用完都会释放嘛,为什么传指针就可以修改呢

 #include<stdio.h>
 int main()
 {
     int a=1;
     int b=2;
     swap(a,b);
     }
     void wap(int a,int b)
     {
        int t=a;
        a=b;
        b=t;
        }
        汇编:
        main:
 10 .LFB0:
 11         .cfi_startproc
 12         lea     ecx, [esp+4]
 12         lea     ecx, [esp+4]
 13         .cfi_def_cfa 1, 0
 14         and     esp, -16
 15         push    DWORD PTR [ecx-4]
 16         push    ebp
 17         .cfi_escape 0x10,0x5,0x2,0x75,0
 18         mov     ebp, esp
 19         push    ecx
 20         .cfi_escape 0xf,0x3,0x75,0x7c,0x6
 21         sub     esp, 20
 22         mov     DWORD PTR [ebp-16], 1
 23         mov     DWORD PTR [ebp-12], 2
 24         sub     esp, 8
 25         push    DWORD PTR [ebp-12]
 26         push    DWORD PTR [ebp-16]
 27         call    swap
 28         add     esp, 16
 29         sub     esp, 4
 30         push    DWORD PTR [ebp-12]
 31         push    DWORD PTR [ebp-16]
 32         push    OFFSET FLAT:.LC0
 33         call    printf
 34         add     esp, 16
 35         mov     eax, 0
 36         mov     ecx, DWORD PTR [ebp-4]
 37         .cfi_def_cfa 1, 0
 38         leave
 39         .cfi_restore 5
 40         lea     esp, [ecx-4]
 41         .cfi_def_cfa 4, 4
 42         ret
 43         .cfi_endproc
 44 .LFE0:
 45         .size   main, .-main
 46         .globl  swap
 47         .type   swap, @function
 48 swap:
 49 .LFB1:
 50         .cfi_startproc
 51         push    ebp
 52         .cfi_def_cfa_offset 8
 53         .cfi_offset 5, -8
 54         mov     ebp, esp
 55         .cfi_def_cfa_register 5
 56         sub     esp, 16
 57         mov     eax, DWORD PTR [ebp+8]
 58         mov     DWORD PTR [ebp-4], eax
 59         mov     eax, DWORD PTR [ebp+12]
 60         mov     DWORD PTR [ebp+8], eax
 61         mov     eax, DWORD PTR [ebp-4]
 62         mov     DWORD PTR [ebp+12], eax
 63         nop
 64         leave
 65         .cfi_restore 5
 66         .cfi_def_cfa 4, 4
 nop
 leave
  ret                                                             

我觉得引用和指针也差不多,,,
#include<stdio.h>
 int main()
 {
     int a=1;
     int b=2;
     swap(&a,&b);
     }
     void wap(int *a,int *b)
     {
        int t=*a;
        *a=*b;
        *b=t;
        }
汇编:
main:
.LFB0:
    .cfi_startproc
    lea ecx, [esp+4]
    .cfi_def_cfa 1, 0
    and esp, -16
    push    DWORD PTR [ecx-4]
    push    ebp
    .cfi_escape 0x10,0x5,0x2,0x75,0
    mov ebp, esp
    push    ecx
    .cfi_escape 0xf,0x3,0x75,0x7c,0x6
    sub esp, 20
    mov eax, DWORD PTR gs:20
    mov DWORD PTR [ebp-12], eax
    xor eax, eax
    mov DWORD PTR [ebp-20], 1
    mov DWORD PTR [ebp-16], 2
    sub esp, 8
    lea eax, [ebp-16]
    push    eax
    lea eax, [ebp-20]
    push    eax
    call    swap
    add esp, 16
    mov edx, DWORD PTR [ebp-16]
    mov eax, DWORD PTR [ebp-20]
    sub esp, 4
    push    edx
    push    eax
    push    OFFSET FLAT:.LC0
    call    printf
    add esp, 16
    mov eax, 0
    mov ecx, DWORD PTR [ebp-12]
    xor ecx, DWORD PTR gs:20
    je  .L3
    call    __stack_chk_fail
.L3:
    mov ecx, DWORD PTR [ebp-4]
    .cfi_def_cfa 1, 0
    leave
    .cfi_restore 5
    lea esp, [ecx-4]
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .globl  swap
    .type   swap, @function
swap:
.LFB1:
    .cfi_startproc
    push    ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    mov ebp, esp
    .cfi_def_cfa_register 5
    sub esp, 16
    mov eax, DWORD PTR [ebp+8]
    mov eax, DWORD PTR [eax]
    mov DWORD PTR [ebp-4], eax
    mov eax, DWORD PTR [ebp+12]
    mov edx, DWORD PTR [eax]
    mov eax, DWORD PTR [ebp+8]
    mov DWORD PTR [eax], edx
    mov eax, DWORD PTR [ebp+12]
    mov edx, DWORD PTR [ebp-4]
    mov DWORD PTR [eax], edx
    nop
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc

你那个不是引用,而是传指针
从汇编的角度看,当然是差不多的,都是传了一个数字嘛。
只是这个数字代表的含义不同,前者是具体的值,后者是一个地址。(当然,从指针的指针int **的角度看,int 也是一个具体的值,站在 int **的角度看,int **也是具体的值)

因为int *是指针,如果你把它当作具体的值看待,那么和int是一样的。
swap(int * a, int * v)
{
int t;
a = &t;
b = a;
a = t;
}
这个代码就和你
swap(int a, int b)
一样,虽然修改了a, b,但是不会有效果。

而swap(int * a, int * b)
{
int t;
t = *a;
*a = *b;
*b = t;
}
之所以能成功,是因为它并没有修改a,b,而是修改的是a b指向的对象

你仔细对比这两个程序,就明白了。

简单总结下就是,无论是int 还是int *,编译器产生的传参数代码(也就是push)是差不多的。
任何时候修改函数的参数,都不会作用到原来的调用者(除非用c++的引用)
这里说的修改参数是指参数本身。
int * a,对它的修改是 a = xxx,也就是让它指向另一个对象。
*a = xxx,并没有修改a,修改的是a指向的对象。这其实和函数参数没关系。

这就看你对指针和引用的区别了,从语言层面看,引用好像没有分配内存,只是被引用变量的别名,但是从汇编底层实现看,其实引用和指针是一样的,所以
从汇编看你觉得引用和指针没什么区别是对的,其实引用&p = a; 就相当于 *p = &a,只是语言层面,编译器做了一层透明,对我们开发人员来说是透明的,
就可以理解为引用就是别名。

从C言角度来分析,函数参数都是在栈里分配一个临时变量存着传过来的值,如果传过来是指针,变量存放着的是一个指针,指向传过来的参数地址;如果传过来
是引用,变量存放着的是一个地址,这个地址是传过来的参数地址。
指针:是一个变量,可以指向任何变量的地址
地址:是一个常量,是唯一的,不可修改的
所以是不同的,但是作用是相同的,因为引用是直接操作,指针是间接操作,要经过指针指向的地址。从内存角度来看,都是相同的,从速度来看,引用优于指针。