C语言函数传参方式

abstract

在 C 语言中,所有函数参数都是通过“值传递”方法传递的。这意味着函数接收的是参数值的副本,而不是原始变量的引用,这防止了函数修改原始变量的值

在C 语言中,被调用函数不能直接修改主调函数中变量的值(比如main函数定义的变量n,传参给main内部调用的power函数(power(base,n)后面的示例代码给出实现),power内部无论对n怎么修改,main中的n取值都不会发生改变,也就是说,被调用函数(比如这里的power)只能修改其私有的临时副本的值main无法得知power函数中的n的取值,除非powern返回出来或通过其他方式将其私有变量的取值传递出来(比如通过外部变量的指针,将私有值通过指针写入主调函数可以访问变量中)

传参类型

在C语言中,函数传递参数(passing arguments)的主要方式有两种:值传递(Pass by Value)指针传递(Pass by Pointer)。这两种方式有不同的内存处理机制,影响着函数对数据的操作能力。

指针传递本质上也是值传递,但是指针传递方式传递的是地址,这个地址可以是通过取地址运算符构成的地址&var的方式提供,也可以是外部用一个指针变量p初始化为&var,然后将p作为参数传递给被调函数

指针传递可以修改的是传入的指针(地址)对应的内存中存储的内容,而无法修改传入的地址本身(这通常没有意义);

例如,通过&var指针传参给被调函数,被调函数可以利用这个指针(地址)修改对应的内存内容,把主调函数中定义的var变量取值给改掉,但是&var不受影响,只是&var所指的内存内容被修改;

如果是主调函数用一个指针变量来保存变量地址,例如int var=1;int p=&var,这时变量p保存的是var变量的地址,而不是var变量的取值;将p传递给被调函数实际传递的也是&var这个地址值,被调函数可以通过p修改var的取值,但是被调函数无法修改主调函数中定义的p,被调函数只能得到p的一个副本(被调函数私有的副本),主调函数中的p不受影响;

传值调用的利大于弊

被调用函数中,参数可以看作是便于初始化的局部变量,因此
额外使用的变量更少。这样程序可以更紧凑简洁。主调函数中的变量不会被意外地受被掉函数的影响,除非你通过其他方法刻意在被调函数中修改定义在主调函数中的变量,例如通过一个swap(a,b)函数交换主调函数中的两个变量的取值

比如,下面的这个power函数利用了这一 性质:

示例代码

下面的代码示例展示了一个函数 power,该函数将基数 base 提升到 n 次方:

/* power: raise base to n-th power; n >= 0;*/
int power(int base, int n)
{
    int p;

    for (p = 1; n > 0; --n)
        p = p * base;
    return p;
}

其中,参数n用作临时变量,并通过随后执行的for循环语句递减,直到其值为0,这样就不需要额外引入变量i

power函数内部对n的任何操作不会影响到调用者函数中n的原始值

必要时,也可以让函数能够修改主调函数中的变量。这种情况下,调用者需要向被调用函数提供待设置值的变量的地址(从技术角度看,地址就是指向变量的指针),而被调用函数则需要将对应的参数声明为指针类型,并通过它间接访问变量。

数组传参

如果是数组参数,情况就有所不同了。当把数组名用作参数时,传递给函数的值是数组起始元素的位置或地址——它并不复制数组元素本身。

在被调用函数中,可以通过数组下标访问或修改数组元素的值。

两种传参方式总结对比表格

传参方式描述是否影响实参适用场景
值传递传递数据的拷贝,主调函数中定义的变量不会受被调函数修改保持主调函数的变量不被修改
指针传递传递数据的内存地址(指针),被调函数可以通过指针修改指针(地址)所指变量的值(如果实参是指针变量,那么指针变量本身也是值传递,不会被修改)修改原始数据、传递大数据结构、返回多个结果等

示例

#include <stdio.h>

#define iprint(expr) printf(#expr " = %i\n", expr) // 整型值调试宏:打印整数表达式

#define xprint(expr) printf(#expr " = %#x\n", expr) // 整型值调试宏:打印整数
int g = 1;
void changePointer(int *pp, int *q)
{
    // 我们知道通过指针传参,可以修改实参所指地址对应的变量取值(不是实参本身的取值)
    // 这里修改函数形参指针pp,看看会不会影响pp对应的实参的取值
    // 结论是不会影响到实参本身(尽管可以通过指针修改主调函数中定义的变量取值)
    xprint(pp);
    *pp = *q;
    pp = q;
    xprint(pp);
}
void changePP(int **pp, int *q)
{
    // 通过指针传参,可以修改实参所指地址对应的变量取值(不是实参本身的取值)
    // 这里修改函数形参指针pp,看看会不会影响pp对应的实参的取值
    // 结论是会影响到实参本身(尽管可以通过指针修改主调函数中定义的变量取值)
    xprint(pp);
    xprint(*pp);
    *pp = q; // 修改pp所指的指针的取值(修改指针变量需要二级指针参数)
    xprint(*pp);
}
void changeG(int v)
{
    // 全局变量g不通过显式传参,而是函数内部直接能够访问到外部变量g,并且可以直接修改
    // 因此滥用全局变量,会带来意外的混乱发生
    g = v;
}
void changeArr(int arr[], int v)
{
    arr[0] = v;
}
int main()
{
    int a = 10;
    int b = 20;
    int c[] = {1, 2, 3};

    int *p = &a;
    // 检查主调函数中定义的变量地址和取值
    iprint(a);
    iprint(b);
    xprint(&a);
    xprint(&b);
    xprint(p);
    // 试验1
    //  试验指针传参会不会修改p本身,以及p所指的变量会不会被修改
    changePointer(p, &b);
    // 可以验证,p本身不会被修改,而p所指的变量*p会被修改,本例中主调函数的变量a被修改为20
    xprint(p);
    iprint(a);

    // 试验2
    // changePP(&p, &b);
    // xprint(p);
    // iprint(*p);

    // 试验3
    // changeG(b);
    // iprint(g);
    // 试验4
    changeArr(c, b);
    for (size_t i = 0; i < sizeof(c) / sizeof(int); i++)
    {
        printf("c[%d]=%d\n", i, c[i]);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cxxu1375

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值