购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.4 参数传递

在程序设计过程中,参数传递是经常会遇到的情况。在C语言中,函数的参数传递的方式通常有两种,一种是传值的方式;另一种是传地址的方式。本节主要介绍传值调用和传地址调用。

2.4.1 传值调用

在函数调用时,一般情况下,调用函数和被调用函数之间会有参数传递。调用函数后面括号里面的参数是 实际参数 ,被调用函数中的参数是 形式参数 。传值调用是建立参数的一个副本并把值传递给形式参数,在被调用函数中修改形式参数的值,并不会影响到调用函数实际参数的值。

【例2-13】 编写一个函数,求两个整数的最大公约数。

【分析】 通过传值调用的方式,把实际参数的值传递给形式参数,其实形式参数是实际参数的一个副本(拷贝)。其程序实现如下。


#include <stdio.h>                              /*
包含输入输出函数*/
int GCD(int m,int n);                           /*
求两个整数的最大公约数的函数声明*/
void main()
{
                int a,b,v;
                printf("
请输入两个整数:");
                scanf("%d,%d",&a,&b);
                v=GCD(a,b);                                     /*
调用求两个数中的较大者的函数*/
                printf("%d
和%d
的最大公约数为:%d\n",a,b,v);
}
int GCD(int m,int n)
/*
求两个整数的最大公约数,并返回公约数*/
{
                int r;
                r=m;
                do
                {
                        m=n;
                        n=r;
                        r=m%n;
                }while(r);
                return n;
}

程序的输出结果如图2-42所示。

假设输入两个数15和25,在主函数中,将15和25分别赋值给实际参数a和b,通过语句s=GCD(a,b)调用实现函数GCD(int x,int y)也就是所谓的被调用函数,将15和25分别传递给被调用函数的形式参数m和n。然后求m和n的最大公约数,通过语句return n;将最大公约数5返回给主函数,即被调用函数,因此输出结果为5。

上述函数参数传递属于参数的单向传递,即a和b可以把值分别传递给m和n,而不可以把m和n传递给a和b。在传值调用中,实际参数和形式参数分别占用不同的内存单元,形式参数是实际参数的一个副本,实际参数和形式参数的值的改变都不会相互受到影响,如图2-43所示。这就像有一张身份证原件,它的复印件就是个副本,复印件的丢失不会影响到身份证原件的存在,身份证原件的丢失也不会影响到复印件的存在。

图2-42 求两个整数的最大公约数运行结果

图2-43 参数传递过程

图2-44 形式参数改变后的情况

在调用函数时,形式参数被分配存储单元,并把15和25传递给形式参数,在函数调用结束,形式参数被分配的存储单元被释放,形式参数不复存在,而主函数中的实际参数仍然存在,并且其值不会受到影响。在被调用函数中,如果改变形式参数的值,假设把m和n的值分别改变为20和35,a和b的值不会改变,如图2-44所示。

2.4.2 传地址调用

C语言通过指针(地址)实现传地址调用。在函数调用过程中,如果需要在被调用函数中修改参数值,则需要把实际参数的地址传递给形式参数,通过修改该地址的内容改变形式参数的值,以达到修改调用函数中实际参数的目的。

【例2-14】 编写一个求两个整数较大者和较小者的函数,要求用传地址方式实现。

【分析】 通过传地址调用的方式,把两个实际参数传递给形式参数。在被调用函数中,先比较两个形式参数值的大小,如果前者小于后者,则交换两个参数值,其中,前者为大,后者为小。传地址调用时,在调用函数和被调用函数中,对参数的操作其实都是在对同一块内存操作,实际参数和形式参数共用同一块内存。其程序实现如下。


#include <stdio.h>            /*
包含输入输出函数*/
void Swap(int *x,int *y);       /*
函数声明*/
void main()
{
                int a,b;
                printf("
请输入两个整数:\n");
                scanf("%d,%d",&a,&b);
                if(a<b)
                        Swap(&a,&b);            /*
两个数中如果前者小,则交换两个值,使其较大的保存在a
中较小保存在b
中*/
                printf("
在两个整数%d
和%d
中,较大者为:%d,
较小者为:%d\n",a,b,a,b);
}
void Swap(int *x,int *y)
/*
交换两个数,较大的保存在*x
中,较小的保存在*y
中*/
{
                int z;
                z=*x;
                *x=*y;
                *y=z;
}

程序的运行结果如图2-45所示。

图2-45 传地址方式求两个整数的较大者和较小者的程序运行结果

在主函数中,如果a<b,则调用Swap(&a,&b)函数交换两个数。其中,实际参数是变量的地址,就是把地址传递给被调用函数Swap(int*x,int*y)中的形式参数x和y,x和y是指针变量,即指针x和y指向变量a和b。这样,交换*x和*y的值就是交换a和b的值。函数调用时,实际参数和形式参数的变化情况如图2-46所示。

如果要修改多个参数的值并返回给调用函数,该怎么呢?这就需要将数组名作为参数传递给被调用函数。数组名作为参数传递时,传递的是整个数组。数组名是数组的首地址,如果把数组名作为实际参数,在函数调用时,会把数组的首地址传递给形式参数。这样形式参数就可以根据数组的首地址访问整个数组并对其操作,这是因为整个数组元素的地址是连续的。

下面是一个数组名作为参数传递的例子。

图2-46 实际参数和形式参数的变化情况

【例2-15】 编写函数,要求将数组中的n个元素的值分别减去20。

【分析】 数组名作为参数传递给被调用函数,实际上是把数组的起始地址传递给形式参数。因为数组在内存中存储的连续性,可以利用数组下标和指针访问数组中的每一个元素,这样在被调用函数中就可以对整个数组进行操作,无须将每一个数据元素作为参数传递给被调用函数。将数组名作为参数传递,调用函数和被调用函数都是对占同一块内存单元的数组进行操作。其程序实现如下。


#include <stdio.h>                             /*
包含输入输出函数*/
#define N 10
void SubArray1(int *x,int n);           /*
数组名作为参数的函数原型*/
void SubArray2(int *aPtr,int n);        /*
指针作为参数的函数原型*/
void main()
{
                int a[N]={51,52,53,54,55,56,57,58,59,60};
                int i;
                printf("
原来数组中的元素为:\n");
                for(i=0;i<N;i++)
                        printf("%4d",a[i]);
                printf("\n");
                printf("
数组中的元素第一次减去20
之后为:\n");
                SubArray1(a,N);                         /*
调用SubArray1
:数组名作为参数的函数*/
                for(i=0;i<N;i++)
                        printf("%4d",a[i]);
                printf("\n");
                printf("
数组中的元素第二次减去20
之后为:\n");
                SubArray2(a,N);                         /*
调用SubArray2
:指针作为参数的函数*/
                for(i=0;i<N;i++)     
                        printf("%4d",a[i]);
                printf("\n");
}
void SubArray1(int b[],int n)
/*
数组名作为参数,将数组中的元素减去20*/
{
                int i;
                for(i=0;i<n;i++)
                        b[i]=b[i]-20;
}
void SubArray2(int *aPtr,int n)
/*
指针作为参数,将数组中的元素减去20*/
{
                int i;
                for(i=0;i<n;i++)
                        *(aPtr+i)=*(aPtr+i)-20;
}

程序运行结果如图2-47所示。

该函数以两种方式实现了函数调用,即数组名作为形式参数和指针作为形式参数。在许多情况下,数组和指针效果是一样的。

在没有调用函数SubArray1(int b[],int n)之前,数组a在内存中的情况如图2-48所示。数组元素被保存在一个联系的存储单元中,数组名a指向数组的第一个元素。在调用函数SubArray1(int b[],int n)后,参数传递给数组名b,b也指向数组a的第一个元素,然后对所有元素减去20,如图2-49所示。调用函数SubArray2(int*aPtr,int n)后,参数传递给指针变量aPtr,aPtr也指向了数组a,并再一次把数组元素减去20,如图2-50所示。

图2-47 数组中的元素减去20后的程序运行结果

图2-48 未调用函数前

图2-49 第一次数组元素被减去20后

图2-50 第二次数组元素被减去20后

注意 在传值调用中,参数传递是单向传递,只能由实际参数传递给形式参数,而不能把形式参数反向传递给实际参数。而在传地址调用中,对形式参数的操作,即是对实际参数的操作,它们拥有同一块内存单元,属于双向传递。
l1yjByqgQoLCwEr6jCL/NzHDC7WgTUmyUoy0y+4Ei9n36j50eDCIHzT3Vi1gZIy/

点击中间区域
呼出菜单
上一章
目录
下一章
×