Menu Close

函数和函数调用

C编程中的函数是可重复使用的代码块,使程序更易于理解,测试,并且可以在不更改调用程序的情况下轻松进行修改。 函数对代码进行划分,并对程序进行模块化,以获得更好,更有效的结果。 简而言之,将较大的程序分为各种各样的子程序。这些子程序称为函数。函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数。

您可以把代码划分到不同的函数中。每个函数执行一个特定的任务来进行的。

函数声明告诉编译器函数的名称、返回类型和参数。函数定义提供了函数的实际主体。

C 标准库提供了大量的程序可以调用的内置函数。例如,函数 strcat() 用来连接两个字符串,函数 memcpy() 用来复制内存到另一个位置。

将大块的程序划分为各个小函数时,可以轻松地分别管理每个函数。 每当程序中发生错误时,您都可以轻松研究故障函数并仅仅更正该函数内的那些错误。

您也可以在需要时轻松调用和使用函数,从而自动节省时间和空间。

C语言的函数类型分为两类,一是库函数,二是用户定义函数。

库函数和用户定义函数之间的区别在于,我们不需要为库函数编写代码。 它已经存在于头文件中,我们始终将其包含在程序的开头。 您只需要键入函数的名称,然后将其与正确的语法一起使用即可。 Printf,scanf是库函数的示例。

而用户定义的函数必须由用户自己编写函数的主体,并在需要函数的时候执行某些操作调用该函数。

用户定义的函数始终由用户编写,但以后可以成为用户本身项目的库函数的一部分。 这是“ C”编程的主要优点。

在C语言中,编程功能分为三个活动,例如,

  • 函数声明
  • 函数定义
  • 函数调用

函数声明

函数声明是指编写程序的名称。 函数声明会告诉编译器函数名称及如何调用函数。当您在一个源文件中定义函数且在另一个文件中调用函数时,函数声明是必需的。 在函数声明中,我们仅指定要在程序中使用的函数名称和类型;该声明和变量声明一样。 除非在程序中声明了函数,否则我们无法使用它。 函数声明也称为“函数原型”。

语法

return_data_type function_name (data_type arguments);
  • return_data_type: 函数的数据类型, 该类型和调用语句所需要的值的数据类型是一样的;
  • The function_name: 函数名,后面是小括号
  • Arguments 参数 – 名称及其数据类型声明可以选择放在括号内。

在函数声明中,参数的名称并不重要,只有参数的类型是必需的.当您在一个源文件中定义函数且在另一个文件中调用函数时,函数声明是必需的。在这种情况下,您应该在调用函数的文件顶部声明函数。

请记住,函数不一定返回值。 在这种情况下,将使用关键字void。例如,output_message函数声明该函数未返回值, 就可以使用:void output_message();

函数定义

C 语言中的函数定义的一般形式如下:

return_type function_name( parameter list )
{
   body of the function
}

在 C 语言中,函数由一个函数头和一个函数主体组成。下面列出一个函数的所有组成部分:

  • 返回类型:一个函数可以返回一个值。return_type 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 void。
  • 函数名称:这是函数的实际名称。函数名和参数列表一起构成了函数签名。
  • 参数:参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。
    函数主体:函数主体包含一组定义函数执行任务的语句。

函数定义意味着只编写函数的主体。 函数的主体由要执行特定任务的语句组成。 函数主体由一个或一个语句块组成。 它也是函数的必需部分。

实例:

int add(int a,int b)	//function body	
{
	int c;
	c=a+b;
	return c;
}

调用函数

创建 C 函数时,会定义函数做什么,然后通过调用函数来完成已定义的任务。每当我们调用函数时,它都会执行为其设计的操作。 函数调用是程序中的可选部分。
当程序调用函数时,程序控制权会转移给被调用的函数。被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束括号时,会把程序控制权交还给主程序。
调用函数时,传递所需参数,如果函数返回一个值,则可以存储返回值。

result = add(4,5);

函数定义,函数声明,函数调用的完整的代码:


#include <stdio.h>
int add(int a, int b);	//function declaration
int main()
{
	int a=10,b=20;
	int c=add(10,20); 	//function call
	printf("Addition:%d\n",c);
	getch();
}
int add(int a,int b)	//function body
{
	int c;
	c=a+b;
	return c;
}

 

函数参数 (Function Arguments)

函数的参数用来通过函数调用接收必要的值。 它们按位置匹配: 第一个参数传递给第一个参数,第二个参数传递给第二个参数,依此类推。

默认情况下,参数按值传递,在该值中将数据副本提供给被调用函数。 实际传递的变量不会更改。我们考虑以下程序,该程序演示了按值传递的参数:

int add (int x, int y);
int main()
{
  int a, b, result;
  a = 5;
  b = 10;
  result = add(a, b);
  printf("%d + %d\ = %d\n", a, b, result);
  return 0;
}

int add (int x, int y)
{
  x += y;
  return(x);
}

请记住,传递给add函数的a和b的值不会更改,因为仅将其值传递给了参数x。

实参(argument):
全称为”实际参数”是在调用时传递给函数的参数. 实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。

形参(parameter):
全称为”形式参数” 由于它不是实际存在变量,所以又称虚拟变量。是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数.在调用函数时,实参将赋值给形参。因而,必须注意实参的个数,类型应与形参一一对应,并且实参必须要有确定的值

1. 形参变量只有在被调用时bai才分du配内存单元,在调用结束时,即刻释放所zhi分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。
2. 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使实参获得确定值。
3. 实参和形参在数量上,类型上,顺序上应严格一致,否则会发生类型不匹配”的错误。
4. 函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。 因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。

 

后面需要添加内容例如变量范围,静态变量等。

递归函数

一个函数在它的函数体内调用它自身称为递归调用,这种函数称为递归函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层,当最内层的函数执行完毕后,再一层一层地由里到外退出。

考虑一个数字的阶乘,其计算公式如下6! = 6 * 5 * 4 * 3 * 2 * 1。

重复计算fact*(fact-1)直到fact等于1。

#include <stdio.h>
int factorial(int number);

int main() 
{    
  int x = 6;
  printf("The factorial of %d is %d\n", x, factorial(x)); 
  return 0;
}

int factorial(int number) 
{
  if (number == 1)   
     return (1); /* exiting condition */
  else
    return (number * factorial(number - 1));
} 

factorial() 就是一个典型的递归函数。调用 factorial() 后即进入函数体,只有当 n==1 时函数才会执行结束,否则就一直调用它自身。
由于每次调用的实参为 n-1,即把 n-1 的值赋给形参 n,所以每次递归实参的值都减 1,直到最后 n-1 的值为 1 时再作递归调用,形参 n 的值也为1,递归就终止了,会逐层退出。


A. 声明一个函数,该函数接受一个整数参数并返回该参数的阶乘。此函数将调用自身并减少参数值直到退出或达到基本条件为止。 当条件为真时,先前生成的值将彼此相乘,并返回最终的阶乘值。
B. 声明并初始化一个值为“ 6”的整数变量,然后通过调用阶乘函数来打印其阶乘值

考虑下图,以更深入地了解递归机制,该机制包括调用函数自身直至达到基本情况或停止条件,然后再收集先前的值:

数组名作函数参数

数组名可以作函数的实参和形参。如:

main()
{int array[10];
……
……
f(array,10);
……
……
}

f(int arr[],int n);
{
……
……
}

array 为实参数组名,arr 为形参数组名。在学习指针变量之后就更容易理解这个问题了。数组名就是数组的首地址,实参向形参传送数组名实际上就是传送数组的地址,形参得到该地址后也指向同一数组。这就好象同一件物品有两个彼此不同的名称一样。

同样,指针变量的值也是地址,数组指针变量的值即为数组的首地址,当然也可作为函数的参数使用。

举例

float aver(float *pa);

main()
{
 float sco[5],av,*sp;
 int i;
 sp=sco;
 printf("\ninput 5 scores:\n");
 for(i=0;i<5;i++) scanf("%f",&sco[i]);
 av=aver(sp);
 printf("average score is %5.2f",av);
}

float aver(float *pa)
{
 int i;
 float av,s=0;
 for(i=0;i<5;i++) s=s+*pa++;
 av=s/5;
 return av;
}