变量在函数间的传递

在c语言中,传递在函数间的变量有普通变量和指针变量,在被调函数中想要修改主调函数传来的变量,是我们在实际开发中经常用用到的技术。
1.普通变量的传递(值拷贝)
int test_func(int test_a)
{
    test_a = 5;
    return 0;
}

int main(void)
{
    int main_a = 1;

    test_func(main_a);

    printf("main_a = %d\n", main_a);

    return 0;
}

运行结果:
这里写图片描述

十分简单,test_func函数并不能修改main函数传来的main_a变量。这是因为函数间的传参是值拷贝的方式,main函数在调用test_func函数的时候,把变量main_a拷贝了一份作为副本,并把副本传递给test_func函数,也就是说test_func修改的是main_a的副本,自然,这个修改操作并不能修改到main_a。

2.普通变量的传递(传递值的地址)

上面代码,要实现被调函数能够修改主调函数传来的值,就需要让被调用函数传递待修改的变量的地址

int test_func(int *test_a)
{
    *test_a = 5;
    return 0;
}

int main(void)
{
    int main_a = 1;

    test_func(&main_a);

    printf("main_a = %d\n", main_a);

    return 0;
}

运行结果:
这里写图片描述

同理,函数间的传参是采用拷贝的方式,只不过这里拷贝的是变量的地址,当main函数调用test_func函数时,test_func函数实现的过程为:
(1)定义一个指针变量test_a,它用来存放main函数传来的变量main_a的地址;
(2)对指针变量test_a解引用,并为其赋值为5;
(3)由于test_a指向的地址等于main_a的地址,所以就修改了main_a的值。

3.指针变量在函数间的传递(指针变量拷贝)
int test_func(int *test_p)
{
    int b = 100;
    test_p = &b;

    return 0;
}

int main(void)
{
    int *main_p = NULL;
    int a = 5;

    main_p = &a;

    printf("*main_p = %d\n", *main_p);  //打印p=5

    test_func(main_p);

    printf("*main_p = %d\n", *main_p);

    return 0;
}

运行结果:
这里写图片描述
运行结果表明test_func函数并没有修改main函数中main_p指针变量所指向的值。跟之前的代码类似,分析test_func的执行过程:
(1)main函数调用test_func函数并传递指针变量main_p给它时,它会定义一个指针变量test_p用于接收main_p;
(2)main_p和test_p都是指针变量,根据参数在函数间传递的拷贝原则,test_p存放的是main_p的副本,所以test_func对指针的副本的操作,不会影响到main中指针变量main_p的值,main_p还是等于5。

4.指针变量在函数间的传递(传递指针变量的地址)

我们知道,主调函数想要传递变量的地址给被调函数时,被调函数时用一级指针(*p)来接收;然而,传递指针的地址时,形参则是采用二级指针(**p)来接收。

//**test_p是二级指针,它用于存放一级指针的地址
int test_func(int **test_p)
{
    int b = 100;
    *test_p = &b;

    return 0;
}

int main(void)
{
    int *main_p = NULL;
    int a = 5;

    main_p = &a;

    printf("*main_p = %d\n", *main_p);  //打印p=5

    test_func(&main_p);

    printf("*main_p = %d\n", *main_p);

    return 0;
}

运行结果:
这里写图片描述
test_func函数中,**test_p是一个二级指针,

test_p = &main_p, *test = main_p, **test = *main_p

test_func的执行过程为:
(1)test_func被调用时,它会定义一个二级指针用来接收main函数传来的一级指针main_p的地址,注意,它接收的是main_p的地址的副本;
(2)对二级指针解引用得到main函数main_p的指针值,(它是a变量的地址值)并将其改为b的地址值;
(3)所以在main函数中得到的*main_p就是test_func中变量b的值了。

5.小结

指针变量在函数间的传递跟普通变量在函数间传递是一样的性质的,想要在被调函数中修改主调函数传来的变量值,即必须传递该变量的地址,另外,注意指针变量的地址是用二级指针接收。

6.二级指针在动态分配中的使用

malloc:

void MallocMem(int *p)
{
    p = (int* )malloc(sizeof(int) * 10);
}

int main(void)
{
    int *p = NULL;

    MallocMem(p);

    if (!p)
        printf("malloc failed!!\n");
    else
    {
        printf("malloc successful!\n");
        free(p);
    }   

    return 0;
}

运行结果:
这里写图片描述
malloc函数实现在堆空间的动态分配,完毕后会将分配到的堆空间的首地址赋给一个指针,也就是说它会修改指针变量的值。而在main函数中传来的是一个指针变量,根据拷贝原则,MallocMem接收到的指针变量是main函数传来的指针变量的副本,在MallocMem中是副本去接收malloc分配到的堆空间的首地址,所以main中的指针p还是为NULL。
应该修改为:

void MallocMem(int **p) //二级指针
{
    *p = (int* )malloc(sizeof(int) * 10);  //*p = main函数中的p
}

int main(void)
{
    int *p = NULL;

    MallocMem(&p);

    if (!p)
        printf("malloc failed!!\n");
    else
    {
        printf("malloc successful!\n");
        free(p);
    }   

    return 0;
}

运行结果:
这里写图片描述
free:

void FreeMem(int *p)
{
    free(p);
    p = NULL;   //不报错,但是是无作用的代码
}

int main(void)
{
    int *p = NULL;
    p = (int* )malloc(sizeof(int) * 10);
    if (p == NULL)
    {
        printf("malloc failed!!\n");
        return -1;
    }

    FreeMem(p);

    return 0;   
}

在FreeMem函数中为了防止内存泄漏的出现,需要将free后的指针置为NULL。如上代码即想实现这样的操作,同样的道理,将指针置为NULL也是在修改指针变量,所以不能单纯的传递指针变量,而是要传递指针变量的地址,修改如下:

void FreeMem(int **p)
{
    int *tmp = *p;  //tmp等于main中的p
    free(tmp);
    *p = NULL;  //*p = main中的p,这样才起到避免野指针的效果
}

int main(void)
{
    int *p = NULL;
    p = (int* )malloc(sizeof(int) * 10);
    if (p == NULL)
    {
        printf("malloc failed!!\n");
        return -1;
    }

    FreeMem(&p);

    return 0;   
}

变量在函数间的传递基本就是这些知识点,有需完善的后面会补充。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值