首页 > 编程笔记

C语言函数参数及其传递方式

当主调函数调用被调函数时,它们之间究竟是如何进行信息交换的呢?答案是通过传递函数的参数。可见,参数在函数中扮演着非常重要的角色。

C语言函数的参数

函数的参数有形式参数(简称形参)和实际参数(简称实参)两类。
【示例1】将两个数由小到大排序输出。代码如下:
#include<stdio.h>
void order(int a,int b) /*a,b形式参数*/
{
   int t;
   if(a>b)/*如果a>b,就执行以下3条语句,交换a,b的值*/
   {
      t=a;
      a=b;
      b=t;
   }
   printf("从小到大的顺序为:%d  %d\n",a,b); /*输出交换后的a,b的值*/
}
int main()
{
   int x,y;
   printf("请输入两个整数:"); /*从键盘输入两个整数*/
   scanf("%d%d",&x,&y);
   order(x,y); /*x,y是实际参数*/
}
运行结果:

请输入两个整数:13 9
从小到大的顺序为:9  13


该程序由两个函数main() 和order()组成。order() 函数定义中的 a 和 b 是形参,在函数调用时接收实参传递过来的值;在 main() 函数中,通过“order(x,y);”调用子函数,其中的 x 和 y 是实参,在主函数中赋值,当函数调用时把值传递给形参 a 和 b。
 
1) 定义函数时,必须说明形参的类型,如【示例1】中,形参 a 和 b 的类型都是整型。

注意:形参只能是简单变量或数组,不能是常量或表达式。

2)  函数被调用前,形参不占用内存的存储单元。函数调用以后,形参才被分配内存单元。函数调用结束后,形参所占用的内存也将被回收,被释放。

3)  实参可以是常量、变量或表达式。如在调用时可写成:

order(2,3);  /*实参是常量*/
order(x+y,x-y); /*实参是表达式*/

如果实参是表达式,先计算表达式的值,再将实参的值传递给形参。但要求它有确切的值,因为在调用时要将实参的值传递给形参。

4) 实参的个数、顺序和类型应该与函数定义中形参表中的形参个数、顺序和类型一一对应。

如上面代码中的 order() 函数,定义时有两个整型的形参,调用时,实参也要与它对应,即两个整型的实参,而且多个实参之间要用逗号隔开。如果不一致,则会发生“类型不匹配”的错误。

注意:对于特殊的字符型和整型,是可以互相匹配的,必要的时候还需要进行类型的转换。

C语言函数参数的传递方式

形参只是一个形式,在调用之前并不分配内存。

函数调用时,系统为形参分配内存单元,然后将主调函数中的实参传递给被调函数的形参。被调函数执行完毕,通过 return 语句返回结果,系统将形参的内存单元释放。

由此可见,实参和形参的功能主要是数据传递,按照传递的是“数据”还是“地址”,分为“值传递”和“地址传递”。

“值传递”是“单向传递”,“地址传递”是“双向传递”。顾名思义,“单向传递”只能把实参的值传递给形参,形参的值不能回传给实参,而“双向传递”既可以把实参的值传递给形参,也可以把形参的值回传给实参。

下面就来了解一下这两种参数的传递方式。

1)“值传递”——单向传递

C 语言规定,实参对形参的数据传递是“值传递”,即单向传递,只能把实参的值传递给形参,而不能把形参的值再传回给实参。在内存当中,实参与形参占用不同的单元,不管名字是否相同,因此函数中对形参值的任何改变都不会影响实参的值。

【示例2】使用函数交换两个变量的值。代码入下:
#include<stdio.h>
void swap(int a,int b) /*定义swap()函数*/
{
   int temp;
   temp=a;a=b;b=temp;      /*交换a,b值的3条语句*/
   printf("a=%d,b=%d\n",a,b);  /*输出交换后的结果*/
}
int main()
{
   int x,y;
   printf("请输入两个整数:\n");
   scanf("%d%d",&x,&y);      /*输入两个整数*/
   printf("调用函数之前:\n");
   printf("x=%d,y=%d\n",x,y); /*输出调用swap()函数之前x,y的值*/
   printf("调用函数中:\n");
   swap(x,y);               /*调用swap()函数*/
   printf("调用函数之后:\n");
   printf("x=%d,y=%d\n",x,y); /*输出调用swap()函数之后x,y的值*/
}
运行结果:

请输入两个整数:
6 8
调用函数之前:
x=6,y=8
调用函数中:
a=8,b=6
调用函数之后:
x=6,y=8


本例中为什么在 swap() 函数内变量 a 和 b 的值互换了,而主调函数 main() 中实参 x 和 y 却没有交换呢?这是参数按值传递的缘故。

main() 函数中定义的变量 x 和 y 在内存中各自占用了存储单元,在调用 swap() 函数时,为形参 a 和 b 另外分配了内存单元,形参与实参的存储单元是不同的,将 x 的值传给 a,y 的值传给 b,如下图中左图所示。

被调函数的形参是局部变量,只在被调函数内部起作用,且形参的值不能反过来传给主调函数。因此在 swap() 函数执行过程中,尽管把 a 和 b 的值交换了,但不能影响 main() 函数中的实参 x 和 y 的值,如下图中右图所示。函数调用完成,形参的内在单元将被释放。


如图所知,在函数调用过程中,形参的值发生改变,并不会影响实参的值。

提示:在“值传递”的过程中,按参数顺序传递数据,即第 1 个实参传给第 1 个形参,第 2 个实参传给第 2 个形参……与变量名无关,如【示例 2】中的两个形参写成 x 和 y 也仍然是不同的变量,实参和形参各有各的存储单元。形参 a 和 b 交换,并不会影响实参 x 和 y 的值。

C语言变量可分为局部变量和全局变量两种。

2)“地址传递”——双向传递

我们知道,数组名表示的是数组在内存中分配的存储空间的起始地址。

如果把数组名作为参数进行传递就是“地址传递”,即把实参数组的起始地址传递给形参数组。这样形参数组和实参数组就占用了共同的存储空间,在子函数中对形参数组做的任何操作实际上就是对实参数组的操作。

子函数结束时不需要用 return 返回任何数据,当子函数结束后形参数组仍然作为局部变量被释放掉存储空间,返回主函数中继续向下执行代码,这时实参数组的元素已经进行了更新。

例如,主函数中调用子函数的语句如下。

int array[5];
findMax(array);

在这里使用数组名作为参数传递给子函数 findMax(),实参数据类型需要和形参数据类型一致,所以可以这样定义 findMax() 函数的参数。例如:

void findMax(int a[5])

形参数组的长度“5”也可以省略,写成下面的形式。

void findMax(int a[ ])


【示例3】下面通过一个示例,具体说明将数组名和简单变量作为参数传递时实参的值是否会被改变。
#include <stdio.h>
#define MAXELS 5
void findMax(int [ ],int);      /*声明函数*/
int main()
{
  int nums[MAXELS] = {0};       /*数组初始化*/
  int i,value=0;
  printf("调用函数前输出结果:\n");
  for (i = 0; i < MAXELS; i++)
    printf("nums[%d] = %d\n", i,nums[i]);
  printf("value = %d\n", value);
  findMax(nums,value);       /*调用函数,传递数组名和简单变量*/
  printf("调用函数后输出结果:\n");
  for (i = 0; i < MAXELS; i++) /*循序输出数组元素*/
    printf("nums[%d] = %d\n", i,nums[i]);
  printf("value = %d\n", value);
  return 0;
}
void findMax(int vals[ ],int m)  /* 查找最大值函数 */
{
  int i;
  m=1;
  printf("findMax输出结果:\n");
  for (i = 0; i < MAXELS; i++)
  {
    vals[i] = i;
    printf("vals[%d] = %d\n", i,vals[i]);
  }
  printf("max=%d\n m = %d\n", vals[--i],m);
}
运行结果:

调用函数前输出结果:
nums[0] = 0
nums[1] = 0
nums[2] = 0
nums[3] = 0
nums[4] = 0
value = 0
findMax输出结果:
vals[0] = 0
vals[1] = 1
vals[2] = 2
vals[3] = 3
vals[4] = 4
max=4
m = 1
调用函数后输出结果:
nums[0] = 0
nums[1] = 1
nums[2] = 2
nums[3] = 3
nums[4] = 4
value = 0

观察结果,可以看到结果就是我们预期的。主函数在调用前,输出的数组元素值是 0,调用后输出的是 0~4。主函数的变量value 在调用前后没有改变,都是0。

从结果分析,数组名是用地址传递方式进行的函数调用,形参和实参指向的是内存中的同一个存储区。

C语言带参数的主函数

从开始学 C 语言,我们就一直使用 main() 函数,都知道一个 C 程序必须有并且仅有一个主函数,C 程序的执行总是从 main() 函数开始的。

main() 函数在使用过程中应该注意以下几点。
  1. main() 函数可以调用其他函数,包括本程序中定义的函数和标准库中的函数,但其他函数不能反过来调用 main() 函数。main() 函数也不能调用自己。 
  2. 我们应该知道main() 函数没有在函数头中提供参数。其实,main() 函数可以带有两个参数,其一般形式如下。

int  main(int  argc,char  *argv[ ])
{
   函数体
}

其中,形参 argc 表示传给程序的参数个数,其值至少是 1 ;而 argv[] 则是指向字符串的指针数组。

注意:如果读者熟悉 DOS 的行命令操作系统,就会知道使用计算机命令是在提示符后面输入相应的命令名;如果有参数,就在命令后面输入相应的参数(如文件名等),并且命令与各参数之间用空格隔开,最后按【Enter】键运行该命令。

用户编写的 C 程序经过编译、连接后形成的可执行文件,可以像命令一样使用,其后面当然也可以跟命令行的参数,这个参数就传递给了 main() 函数。

【示例4】输出包含几个字符串的一行文字。串之间用空格隔开,有几个字符串就相当于有几个参数。
#include <stdio.h>
int main(int argc, char *argv[])
{
   int count;
   printf("The command line has %d arguments: \n",argc-1);
   for(count=1;count<argc;count++)  /*依次读取命令行输入的字符串*/
      printf("%d: %s\n",count,argv[count]);
}
运行结果:

The command line has 3 arguments:
1: I
2: am
3: happy!

本例中,程序从命令行中接收 3 个字符串(相当于给 main() 函数传递了 3 个参数),并将它们存放在字符串数组中,其对应关系如下。

argc 的值即是参数的个数,程序在运行时会自动统计。

 

在命令行中的输入都将作为字符串的形式存储于内存中。也就是说,如果输入一个数字,那么要输出这个数字,就应该用 %s 格式,而非 %d 格式或者其他。

main() 函数也有类型。如果它不返回任何值,就应该指明其类型为 void;如果默认其类型为 int,那么在该函数末尾应由 return 语句返回一个值,例如 0。

优秀文章