zoukankan      html  css  js  c++  java
  • 函数参数的传值和传指针有什么区别?

    前言

    我们可能听过C语言中的传值和传指针,在其他语言中,也有传引用一说,那么他们到底有什么区别呢?如果你还不能准确地分辨,就该好好了解一下了。

    传值

    我们在初学C语言的时候就被老师教过,下面的方式是无法交换a和b的值的:

    #include<stdio.h>
    void swap(int a,int b)
    {
        int temp = a;
        a = b;
        b = temp;
        printf("swap a = %d,b = %d
    ",a,b);
    }
    int main(void)
    {
        int a = 10;
        int b = 20;
        printf("before swap:a = %d,b = %d
    ",a,b);
        swap(a,b);
        printf("after  swap:a = %d,b = %d
    ",a,b);
        return 0;
    }
    

    运行结果如下:

    before swap:a = 10,b = 20                                                                                                                                                                   
    internal swap a = 20,b = 10                                                                                                                                                                 
    after  swap:a = 10,b = 20 
    

    可以看到,a和b的值最终并没有被交换。开始时a,b的值为10,20,而最终还是同样的值。

    为什么呢?因为函数参数在传递的时候,都是传原数据的副本,也就是说,swap内部使用的a和b只是最初始a和b的一个副本而已,所以无论在swap函数内部对a和b做任何改变,都不会影响初始的a和b的值。

    正因如此,我们常常被告知,不要把直接把结构体直接作为参数,这样效率会很低。由于结构体本身占用字节数较大,如果直接作为参数,那么将会产生一个较大的”副本“,如此一来,效率也就很低了。

    我们再结合下面的图来理解:

    首先图中方框中的上部分a和b代表了main函数中的a和b,即原始数据,而方框中的下部分a和b代表了函数的参数a和b,即原始数据的“副本”。(后面的图都是如此,上部分代表原始值,下部分代表函数参数值)。

    调用swap函数前后的情形如下:

    由于在swap中永远只是对a和b的副本进行操作,因此完全不影响原始的a和b的值。最终也不可能达到交换a和b的值的目的。

    传指针

    那么为解决上面的问题,我们知道,需要传指针。其代码如下:

    #include<stdio.h>
    void swap(int *a,int *b)
    {
        int temp = *a;
        *a = *b;
        *b = temp;
        printf("swap a = %d,b = %d
    ",*a,*b);
    }
    int main(void)
    {
        int a = 10;
        int b = 20;
        printf("before swap:a = %d,b = %d
    ",a,b);
        swap(&a,&b);
        printf("after  swap:a = %d,b = %d
    ",a,b);
        return 0;
    }
    

    运行结果:

    before swap:a = 10,b = 20                                                                                                                                                                   
    swap a = 20,b = 10                                                                                                                                                                          
    after  swap:a = 20,b = 10 
    

    可以看到在这种情况下,a,b的值才是真正交换了。
    看到这里,不知道你是否会疑惑,为什么给函数传递参数的时候,一会是传值,一会是传指针呢?为什么传指针就能改变参数的值呢?实际上,C语言里,参数传递都是值传递!也就是说,你认为的传指针也是传值,只不过它的值是指针类型罢了。

    我们再通过图来理解前面为什么传指针就可以交换a,b的值:

    从图中可以看出,虽然传递给函数的是指向a和b的指针的副本,但是它的副本同样也是指向a和b,因此虽然不能改变指针的指向,但是能改变参数a和b指向的内容,即改变原始a和b的值。

    再看传指针

    如果是为指针p申请一段内存,下面的代码能达到目的吗?

    #include<stdio.h>
    #include<stdlib.h>
    void getMemery(int *p)
    {
        /*申请1024个int大小*/
        p = malloc(sizeof(int)*1024);
        if(NULL == p)
        {
            printf("malloc failed
    ");
            p = NULL;
        }
    }
    int main(void)
    {
        int *p = NULL;
        getMemery(p);
        printf("address of p is %p
    ",p);
        return 0;
    }
    

    通过前面的内容分析,肯定是达不到预期效果的。
    运行结果:

    address of p is (null)
    

    这是为什么呢?我们还是利用前面所知来分析,由于传递给getMemory函数的参数都是一个副本,因此函数内的p也是外部p的一个副本,因此即便在函数内部,将p指向了一块新申请的内存,仍然不会改变外面p的值,即p还是指向NULL。

    如何修改呢?我们需要传入p的地址,即指向int类型指针的指针。

    #include<stdio.h>
    #include<stdlib.h>
    void getMemery(int **p)
    {
        /*申请1024个int大小*/
        *p = malloc(sizeof(int)*1024);
        if(NULL == *p)
        {
            printf("malloc failed
    ");
            *p = NULL;
        }
    }
    int main(void)
    {
        int *p = NULL;
        getMemery(&p);
        printf("address of p is %p
    ",p);
        free(p);
        p = NULL;
        return 0;
    }
    

    运行结果如下:

    address of p is 0x144f010
    

    从运行结果可以看到,p的值被改变了,而不再是初始的NULL。

    可配合下面的图进行理解:

    总结

    本文总结如下:

    • 函数的参数都是原数据的“副本”,因此在函数内无法改变原数据
    • 函数中参数都是传值,传指针本质上也是传值
    • 如果想要改变入参内容,则需要传该入参的地址(指针和引用都是类似的作用),通过解引用修改其指向的内容
    • 以上结论不限于C语言
  • 相关阅读:
    python 学习 3-1 (编码)
    mongdb备份
    docker 部署redis , mongodb ,rabbitmq
    python学习第一天第二天总结
    python 学习 (1-3)
    python学习(day1-2)
    Activiti工作流搭建教程
    docker Compose安装
    CAS 单点登录(代码部分)
    推送自定义docker镜像到阿里云
  • 原文地址:https://www.cnblogs.com/Hijack-you/p/11962941.html
Copyright © 2011-2022 走看看