C语言学习笔记之(函数)序

C语言中函数的定义是相互平行的,在定义函数时,一个函数不能包含另一个函数,但是,一个函数在被调用的过程中可以调用其他函数,这就是函数的嵌套调用。

一、函数的嵌套调用

例子:计算1!+2!+3!+…+10!的值并输出,使用函数的嵌套调用实现。

#include <stdio.h>
#define N 10						/*宏定义*/
int main()
{
	float sum(int n);					/*对sum函数进行声明*/
	printf("1!+2!+3!…10!=%-12.5le\n",sum(N));	               /*调用sum函数*/
	return 0;
}
float sum(int n)						/*定义sum函数,求累加*/
{
              float fac(int k);						/*对fac函数进行声明*/
	int i;
	float s=0;
	for(i=1;i<=n;++i)
		s+=fac(i);					/*调用fac函数*/
	return s;
}
float fac(int k)							/*定义fac函数,计算阶乘*/
{
	int i;
	float t=1;
	for(i=2;i<=k;++i)
		t*=i;
	return t;
}

二、函数的递归调用

C语言支持函数的递归调用,所谓递归就是函数直接或间接地调用其本身。直接递归调用如左图所示,在调用f函数的过程中,又调用f函数。间接递归调用如右图所示,在调用f函数的过程中要调用f1函数,而在调用f1函数的过程中又要调用f函数。

例子:用递归函数实现例6-11中提出的求Fibonacci数列的前20项。

#include <stdio.h>
int f(int n)					/*定义函数f用于计算数列的每一项*/
{
	if(n==1||n==2)				/*当n==1或者n==2时,返回1*/
		return 1;
	else
		return (f(n-2)+f(n-1));		/*递归调用*/
}
int main()
{
	int num=20;
	int i;
	for(i=1;i<=num;i++)			/*循环20次*/
	{
		printf("%-6d",f(i));			/*调用f函数*/
		if(i%4==0)			/*每输出4个数后输出一个回车符*/
			printf("\n");
	}
	return 0;
}

三、数组作为函数的参数

数组元素可以作为函数的实参,其用法与普通变量相同,是单向传递,即“值传递”方式;但数组元素不能作为函数的形参使用。

例子1:有下列代码,试分析输出结果。

#include<stdio.h>
void swap(int i,int j)
{
	int t;
	t=i;
	i=j;
	j=t;
	printf("In function i=%d,j=%d\n",i,j);
}
int main()
{
	int a[2]={4,9};
	printf("Before function a[0]=%d,a[1]=%d\n",a[0],a[1]);
	swap(a[0],a[1]);
	printf("After function a[0]=%d,a[1]=%d\n",a[0],a[1]);
	return 0;
}

主函数中定义了一个只有两个数据元素的数组a,并赋初值a[0]等于4,a[1]等于9。毫无疑问,主函数中的第一次输出a[0]=4,a[1]=9。

然后调用swap函数,将a[0]和a[1]作为实参传递给swap函数中的形参i和j,这里定义的swap函数实现交换两数的功能,因此,在swap函数中输出结果为:i=9,j=4。

由于数组元素作为函数的实参时其用法与普通变量相同,是单向传递的,也就是说,只能从实参传递给形参,因此,在调用完swap函数后,主函数中的a[0]和a[1]的值没有发生改变,故主函数中第二次输出结果还是a[0]=4,a[1]=9。

例子2:修改例8-10程序,交换a[0]和a[1]的值。

除了可以用数组元素作为函数参数外,还可以用数组名作为函数参数。数组名既可以作为实参也可以作为形参。

#include<stdio.h>
void swap(int x[2])					/*定义函数实现交换x[0]和x[1]*/
{	int t;
	t = x[0];						/*交换x[0]和x[1]*/
	x[0] = x[1];
	x[1] = t;
	printf("In function x[0]=%d,x[1]=%d\n",x[0],x[1]);	/*输出x[0]和x[1]*/
}
int main()
{	int a[2]={4,9};					/*定义数组a并初始化*/
	printf("Before function a[0]=%d,a[1]=%d\n",a[0],a[1]);	 /*输出a[0]和a[1]*/
	swap(a);						/*调用函数,数组名作为函数实参*/
	printf("After function a[0]=%d,a[1]=%d\n",a[0],a[1]);	/*输出a[0]和a[1]*/
	return 0;
}
  1. 用数组名作函数参数,应该在主调函数和被调用函数中分别定义数组,本例中x是形参数组名,a是实参数组名,分别在其所在函数中定义,不能只在一方定义。

  2. 实参数组与形参数组数据类型应一致(这都为整型),如不一致,结果将出错。

  3. 数组名作函数参数时,是把实参数组的起始地址传递给形参数组,这样两个数组将共占同一段内存单元,从而达到“地址传递”的效果。假如数组a的起始地址为2000,则x数组的起始地址也是2000,显然,数组a和x将共占同一段内存单元,a[0]和x[0]共占同一个单元。由此可见,形参数组中各元素的值如发生变化,将会使实参数组元素的值同时发生变化。因此,调用swap函数可实现a[0]和a[1]的交换。

  4. 在定义swap函数时,声明形参数组x的大小为2,但实际上,指定其大小是不起作用的,因为C编译系统并不检查形参数组的大小,只是将实参数组的首地址传给形参数组。因此,形参数组可不指定大小,在定义数组时在数组名后跟一个空的方括号即可。例如,上述的函数头可写成:

    void swap(int x[]) 定义swap函数,形参数组不指定大小

    例子3:编写函数,统计某一维数组中非0元素的个数。

    #include <stdio.h>
    #define N 5
    count(int a[],int n)						/*定义函数,形参为数组名和数组长度*/
    {
    	int num=0,i;
    	for(i=0;i<n;i++)					/*计算非0个数*/
    		if(a[i]!=0)
    			num++;
    		return num;				/*返回num的值*/
    }
    int main()
    {
    	int b[N],i,sum;
    	printf("enter N integer numbers:");
    	for(i=0;i<N;i++)
    		scanf("%d",&b[i]);				/*输入数组元素*/
    	sum=count(b,N);					/*函数调用,实参为数组名和数组长度*/
    	printf("The number is %d\n",sum);               	              /*输出结果*/
    	return 0;
    }