版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第5章函数第5章函数5.1.1函数概述5.1.2无参函数的定义5.1.3有参函数的定义5.1函数的定义第5章函数函数是C语言程序的基本模块,用户可以把自己的算法编写成一个个相对独立的函数模块。在设计一个较大的程序时,往往会把程序分为若干个程序模块,每个模块包括一个或多个函数,每个函数实现一个特定的功能。一个C程序可由一个主函数和若干个其他函数构成。由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函数调用多次。1.函数的概念函数本质上是一段可以被重复调用的、功能相对独立的程序段,函数的引入实现了两个目的:一是便于结构化程序设计的需要;二是可以提高代码的重复性。(1)便于结构化程序设计的需要。结构化程序设计是一种编程范式,其核心内容是:自顶向下,逐步细化和模块化,强调将程序分解为较小的、易于管理的部分,即把一个大程序按功能分为若干个小程序(模块),每个模块完成一部分程序功能,分而治之,各模块各司其职,结构清晰。这种方法通过使用顺序、选择(条件)和循环三种基本控制结构来构建程序,使得代码更加清晰、易于理解和维护。(2)可以提高代码的重复性。提高代码的重复使用性(或称为代码复用)是通过多种编程实践和技术实现的。这样做不仅能够减少开发时间,还能提高代码的可靠性和可维护性。在程序编写时,可以把经常用到的完成某种相同功能的程序代码段编写成函数,每当需要完成这一功能时调用这个函数即可。如需修改,只需要修改这个函数本身,而调用函数的语句不必修改。5.1函数的定义5.1.1函数概述第5章函数2.函数的分类从用户使用的角度来看,函数可分为标准函数和用户自定义函数。(1)标准函数。标准函数即库函数,标准函数是指由C标准库提供的预定义函数。这些函数被设计用来执行常见的编程任务,如输入函数scanf(),输出函数printf()、计算字符串长度函数strlen()、复制字符串函数strcpy()、数学运算平方根函数sqrt()、正弦函数sin()、余弦函数cos()、正切函数tan()等。应该说明的是,不同的C语言系统提供库函数的数量和功能可能有些差异,但一些基本的标准函数是相同的。(2)用户自定义函数。用户自定义函数是指由程序员根据需要自行编写的函数。这些函数可以用来封装特定的功能,通过合理地定义和使用函数,不仅可以提高代码的复用性,还能增强代码的可读性和可维护性。对于用户自定义函数,不仅要在程序中定义函数本身,而且在主调函数模块中还必须对该被调用函数进行类型说明,然后才能使用。用户自定义函数是C语言编程中的重要组成部分,允许将代码分解成更小、更易于管理的部分。理解如何定义函数、声明函数、传递参数以及处理返回值,是掌握C语言编程的关键技能之一。5.1函数的定义5.1.1函数概述第5章函数在C语言中,无参函数是指那些不接受任何参数的函数。这种类型的函数主要用于执行某些特定的操作或任务,而不需要从调用者那里接收输入数据。1.无参函数的定义无参函数的定义是指其参数列表为空。定义一个无参函数时,在函数名后的括号内留空或者包含void关键字来明确表示该函数不接受任何参数。无参函数的定义格式如下。返回类型函数名(void){//函数体}返回类型指定函数返回值的类型,省略时默认函数返回值为int类型。void表示函数没有参数。函数体包含声明部分和语句部分。声明部分主要是变量的声明或所调用函数的声明;执行部分由执行语句组成,函数的功能正是由这些语句实现的。函数体可以即有声明部分又有语句部分,也可以只有语句部分,还可以两者皆无(空函数)。调用空函数不产生任何有效操作。尽管可以省略void(即只保留空括号),但为了代码的清晰性和可读性,推荐在声明无参函数时显式地使用void。5.1函数的定义5.1.2无参函数的定义第5章函数5.1函数的定义5.1.2无参函数的定义2.无参函数的实例一般情况下,无参函数不会有返回值,此时函数类型名为void。【实例5.1】定义并使用一个无参函数来输出一条问候信息。程序代码如下。
#include<stdio.h>//定义一个无参函数,用于输出问候信息voidgreet(void){printf("Hello,World!\n");}intmain(){greet();//调用无参函数greet()return0;}程序运行结果如下。Hello,World!第5章函数5.1函数的定义5.1.2无参函数的定义需要注意的是无参函数必须使用圆括号。即使函数不接受任何参数,调用它时也必须加上一对圆括号(),这是区分函数与普通变量的关键。如果函数需要返回一个值,则必须指定相应的返回类型(如int,float,char等)。如果函数不需要返回任何值,则应将其返回类型设置为void。当函数定义位于调用它的代码之后时,建议在文件顶部添加函数原型(声明),以便编译器知道函数的存在及其签名。voidgreet(void);//函数声明intmain(){greet();return0;}voidgreet(void){//函数定义
printf("Hello,World!\n");}第5章函数5.1函数的定义5.1.3有参函数的定义在C语言中,有参函数是指那些接受一个或多个参数的函数。这些参数允许函数从调用者那里接收数据,从而使得函数更加通用和灵活。1.有参函数的定义有参函数由返回类型、函数名、参数列表和函数体组成。参数列表列出了传递给函数的所有参数及其类型。每个参数都有一个指定的数据类型和一个名称,在函数体内可以像普通变量一样使用这些参数。有参函数的定义格式如下。返回类型函数名(参数类型参数名1,参数类型参数名2,...){//函数体}有参函数比无参函数多了一项内容,即形式参数列表。在其中给出的参数称为形式参数(简称形参),它们可以是各种类型的变量,各参数之间用逗号分隔。在进行函数调用时,调用函数将赋予这些形参实际的值。函数体包括一对大括号内的若干条语句,这些语句实现了函数的功能,并用return语句返回运算的结果。第5章函数5.1函数的定义5.1.3有参函数的定义2.有参函数的实例有参函数是C语言编程中的重要组成部分,有参函数通过接受参数来增加程序的灵活性和复用性。无论是简单的数值运算还是复杂的数据处理任务,合理地使用有参函数都能显著提高代码的质量和开发效率。【实例5.2】定义一个有参函数,实现两个整数的和。程序代码如下。
#include<stdio.h>//定义一个有参函数,计算两个整数的和intadd(inta,intb){returna+b;}intmain(){intsum=add(10,20);//调用add函数,传递实参10和20printf("sum:%d\n",sum);//输出sum:30return0;}程序运行结果如下。sum:30第5章函数5.2.1函数调用的形式5.2.2函数的返回值5.2.3函数的嵌套与递归调用5.2.4函数声明5.2.5数组作函数的参数5.2函数调用第5章函数在C语言中,函数调用是指程序执行过程中对特定函数的执行请求。通过函数调用,可以将控制权传递给被调用的函数,并且可以向该函数传递参数,以及从该函数接收返回值。1.形式参数和实际参数C语言程序从main()函数开始执行,而自定义函数的执行是通过对自定义函数的调用来实现的。当自定义函数结束时,从自定义函数结束的位置返回到主函数中的调用处继续执行,直到主函数结束。在调用有参函数时,主调用函数和被调用函数之间有数据传递关系。在定义函数进函数名后面括号中的变量称为形式参数(简称形参)。在主调函数中调用一个函数时,函数名后面括号中的参数称为实际参数(简称实参)。实际参数可以主是常量、变量或表达式,各实参之间用逗号分隔,并且实参的个数、类型应该与函数形参的个数、类型一致。如果无参数,则无实际参列表,但括号不能省略。函数的形参和实参具有以下特点。(1)形参只在函数内部有效,即它的作用域仅限于函数体内,形参是局部变量。实参的作用范围取决于其实现的位置(如全局变量、局部变量等)。(2)实参可以是常量、变量或表达式等,只要其类型与形参匹配或者可以隐式转换为形参类型即可。(3)实参的值在函数调用时会被复制到形参中(按值传递),除非使用指针传递地址(按地址传递)。参数传递是“值传递”时,属于单向传递,即实参把值传给形参,但形参的值不能传给实参,也就是说对形参的修改不会影响对应的实参。这是由于在内存中,实参与形参是不同的存储单元,形参在函数被调用之前,形参不占用内存空间。当函数执行完毕后,形参所占用的存储单元被释放。(4)形参和实参在数量、类型和顺序上应严格对应一致,否则会发生类型不匹配的错误。5.2函数调用5.2.1函数调用的形式第5章函数2.形参和实参间的参数传递在调用有参函数时,调用函数与被调用函数之间存在数据传递的关系。调用函数向被调用函数传递数据主要是通过函数的参数进行的,而被调用函数向调用函数传递数据一般是通过return语句来实现的。形参是函数定义时函数名后括号中的变量;实参是指调用函数时函数名后括号中的常量、变量或表达式。在调用函数时,将实参的值传递给形参,使形参在数值上和实参相同。C语言提供了两种参数传递数据方式:按值传递和按地址传递。(1)按值传递。函数调用时,调用函数把实参的值传递给被调用函数的形参,形参值的变化不会影响实参的值。这是一种单向的数据传送方式。当实参是变量、常量、表达式或数组元素,形参是变量名时,函数传递数据采用的是按值传递。5.2函数调用5.2.1函数调用的形式第5章函数【实例5.3】按值传递方式编写C语言程序,实现从键盘输入两个整数,并在函数中交换它们的值。程序代码如下。(插入二维码V5-3按值传递方式编写C语言程序)#include<stdio.h>//交换两个整数voidswap(intx,inty){inttemp;temp=x;x=y;y=temp;printf("x=%d,y=%d\n",x,y);}intmain(){intnum1,num2;printf("请输入第一个整数:");scanf("%d",&num1);printf("请输入第二个整数:");scanf("%d",&num2);printf("交换前:\n");printf("num1=%d,num2=%d\n",num1,num2);swap(num1,num2);printf("交换后:\n");printf("num1=%d,num2=%d\n",num1,num2);return0;}5.2函数调用5.2.1函数调用的形式第5章函数程序运行结果如下。请输入第一个整数:10请输入第二个整数:20交换前:num1=10,num2=20x=20,y=10交换后:num1=10,num2=20程序从main()函数开始执行,当执行“swap(a,b);”语句时,程序首先将实参num1和num2的值10和20分别传递给形参x和y,执行swap()的函数体。在函数体中交换了形参变量x和y的值,因此x的值为20,y的值为10。函数执行完毕后,释放了形参变量x和y的内存空间,返回到主函数。在主函数中num1、num2两个变量的值并没有发生变化,其值仍为10和20。(2)按地址传递。按地址传递通常指的是通过指针来传递参数。这种方式允许被调用的函数直接访问和修改实参变量的值,因为它接收的是指向这些变量的地址(指针),形参和实参共享相同的存储单元,这样通过形参可以直接引用或处理该地址中的数据,以达到改变实参值的目的。这与按值传递不同,按值传递的是实参的副本,因此不能修改原始数据。5.2函数调用5.2.1函数调用的形式第5章函数函数的返回值是指函数执行完毕后返回给调用函数的值。通过返回值,函数可以向调用函数传递结果或状态信息。返回值的数据类型需要在函数定义时指定,并且可以通过return语句返回给调用函数的值。1.return语句返回给调用函数的值函数的值只能通过return语句返回给调用函数。return语句的语法格式如下。return表达式;//返回值必须与函数声明的返回类型匹配(1)在函数中允许有多个return语句,但每次调用逻辑上只能有一个return语句被执行。执行被调用函数时遇到某一个return语句,程序的执行就返回到调用函数中,而被调用函数中return语句之后的语句不会被执行,因此,只能从被调用函数中返回一个函数值。(2)如果不需要从被调用函数返回函数值,可以省略return语句。这时执行函数体中的最后一条语句后,自动退出被调用函数。(3)对不需要返回函数值的函数,可以将其函数类型定义为void(即空类型)。对类型说明符为void的函数,在其函数体中不能使用return语句。5.2函数调用5.2.2函数的返回值第5章函数2.函数值的类型函数的返回值应当属于某一个确定的类型,应该在定义函数时指定函数值的类型。return语句中表达式值的类型应与函数定义中函数的类型保持一致。如果两者不一致,则以函数类型为准,自动转换为函数的类型,即函数类型决定返回值的类型。所以,应当在定义函数时指定函数值的类型。例如,以下函数的首行指定函数返回值的类型。intmax(intx,inty)//函数值为整型charletter(chara1,chara2)//函数值为字符型floatmin(floatx,floaty)//函数值为单精度实型doubleswap(doublex,doubley)//函数值为双精度实型5.2函数调用5.2.2函数的返回值第5章函数【实例5.4】比较两个浮点数并返回最大值的整数部分。程序代码如下。(插入二维码V5-4比较两个浮点数并返回最大值的整数部分)#include<stdio.h>//函数声明:比较两个浮点数并返回最大值的整数部分intmaxInteger(floata,floatb);intmain(){floatnum1,num2;intresult;//输入两个浮点数
printf("请输入第一个实数:");scanf("%f",&num1);printf("请输入第二个实数:");scanf("%f",&num2);//调用函数获取最大值的整数部分
result=maxInteger(num1,num2);//输出结果
printf("两数中较大值的整数部分是:%d\n",result);return0;}//函数定义:返回两个浮点数中较大者的整数部分intmaxInteger(floata,floatb){floatmax=(a>b)?a:b;//取较大的数
returnmax;//系统自动完成类型转换为int类型}5.2函数调用5.2.2函数的返回值第5章函数程序运行结果如下。请输入第一个实数:10.28请输入第二个实数:20.69两数中较大值的整数部分是:20在函数中进行实型运算,希望返回的是整型数值,可让系统自动完成类型转换。但这种做法往往使程序不清晰,可读性降低,容易出错,而且并不是所有的类型都能互相转换的。因此建议初学者不要采用这种方式,而应做到使函数类型与return返回值的类型一致。5.2函数调用5.2.2函数的返回值第5章函数函数的嵌套调用指的是在一个函数内部调用另一个函数。这种机制使得程序可以将复杂的任务分解成多个较小、更易于管理的任务。每个函数负责执行一个特定的功能,通过函数的嵌套调用可以组合这些功能来完成更复杂的工作。在C语言中,函数的定义是互相平行、独立的,也就是说函数不能定义在其他函数内部(即不支持函数的嵌套定义),但可以在一个函数内部调用另一个函数,这称为函数的嵌套调用。5.2函数调用5.2.3函数的嵌套与递归调用第5章函数1.函数的嵌套调用实例【实例5.5】使用函数的嵌套调用,实现从键盘输入三个整数,并找出其中最大的值。程序代码如下。(插入二维码V5-5使用函数的嵌套调用)#include<stdio.h>//声明函数intmaxOfTwo(inta,intb);intmaxOfThree(inta,intb,intc);intmain(){intnum1,num2,num3,max;//输入三个整数
printf("请输入三个整数(空格分隔):");scanf("%d%d%d",&num1,&num2,&num3);//调用函数找出最大值(嵌套调用)
max=maxOfThree(num1,num2,num3);//输出结果
printf("三个数中最大的是:%d\n",max);return0;}5.2函数调用5.2.3函数的嵌套与递归调用第5章函数//返回两个数中较大的一个(被调用函数)intmaxOfTwo(inta,intb){return(a>b)?a:b;}//返回三个数中最大的一个(主调函数,嵌套调用maxOfTwo)intmaxOfThree(inta,intb,intc){intt;t=maxOfTwo(a,b);//第一次调用
returnmaxOfTwo(t,c);//第二次调用}程序运行结果如下。请输入三个整数(空格分隔):105030三个数中最大的是:50maxOfTwo()是最底层的函数,用于比较两个整数;maxOfThree()是中间层函数,它通过两次调用maxOfTwo()来找到三个数的最大值;main()是最高层函数,负责数据输入、调用逻辑函数并输出结果;5.2函数调用5.2.3函数的嵌套与递归调用第5章函数2.函数的递归调用实例在C语言中,函数的递归调用是指一个函数在其定义内部直接或间接地调用自身的过程,称为函数的递归调用。C语言的特点之一就在于允许函数的递归调用。递归是一种强大的编程技术,适用于解决可以被分解为更小的相同问题的任务,如数学计算、数据结构遍历等。【实例5.6】使用函数的递归调用,实现阶乘n!的计算。由于n!=n*(n-1)!,所以要计算n!,就必须先计算(n-1)!,要计算(n-1)!,必须计算(n-2)!,依此类推。要求2!,必须先知道1!,而1!是1,0!也是1。递归终止条件为n=0或n=1。以上关系可以表示如下。5.2函数调用5.2.3函数的嵌套与递归调用第5章函数程序代码如下。#include<stdio.h>//计算n的阶乘longfactorial(intn){//基准情况
if(n==0||n==1){return1;}//递归步骤
returnn*factorial(n-1);}intmain(){intnum;longresult;printf("请输入一个非负整数:");scanf("%d",&num);if(num<0){printf("输入错误:请输入非负整数。\n");}else{result=factorial(num);printf("%d!=%d\n",num,result);}return0;}程序运行结果如下。请输入一个非负整数:55!=1205.2函数调用5.2.3函数的嵌套与递归调用第5章函数【实例5.7】使用函数的递归调用,实现斐波那契数列的前30项,并按照每行5个数值的格式输出这些值,斐波那契数列的递推公式如下。5.2函数调用5.2.3函数的嵌套与递归调用第5章函数程序代码如下。#include<stdio.h>//递归函数:计算第n个斐波那契数longfibonacci(intn){if(n==0)return0;//基准情况1if(n==1)return1;//基准情况2//递归步骤
returnfibonacci(n-1)+fibonacci(n-2);}//打印斐波那契数列,每5个数字换一行voidprintFibonacci(intcount){inti;for(i=0;i<count;++i){printf("%-12d",fibonacci(i));if((i+1)%5==0){//每5个数字后换行
printf("\n");}}if(count%5!=0){//如果最后一行不足5个数字,也换行
printf("\n");}}5.2函数调用5.2.3函数的嵌套与递归调用第5章函数intmain(){intnum;printf("请输入要显示的斐波那契数列的项数:");scanf("%d",&num);if(num<=0){printf("输入错误:请输入正整数。\n");}else{printFibonacci(num);//调用打印函数
}return0;}程序运行结果如下。请输入要显示的斐波那契数列的项数:3001123581321345589144233377610987159725844181676510946177112865746368750251213931964183178115142291213931964183178115142298320405.2函数调用5.2.3函数的嵌套与递归调用第5章函数fibonacci()函数:这是一个递归函数,用于计算给定位置n的斐波那契数值。它有两个基准情况(n==0和n==1),其余情况通过递归调用来解决。printFibonacci()函数:此函数负责生成并打印指定数量的斐波那契数。它使用循环来遍历从0到count-1的所有索引,并调用fibonacci()函数获取每个索引对应的斐波那契数值。每当输出了5个数字时,就会插入一个换行符。如果最后不足5个数字,也会在输出结束后添加一个换行符以保持输出格式的一致性。main()函数:负责从用户那里接收想要显示的斐波那契数列的长度,并检查输入的有效性。然后调用printFibonacci()函数进行实际的计算和输出。5.2函数调用5.2.3函数的嵌套与递归调用第5章函数【实例5.8】定义一个递归函数来计算从1到n之间的整数之和,并在主函数中读取用户输入的n值,然后输出结果。想要计算1~n之和,需要计算1~n-1之和,想要计算1~n-1之和,需要计算1~n-2之和,以此类推,直到递推至n=1时的和为1。1~n之间的整数之和递推公式如下。5.2函数调用5.2.3函数的嵌套与递归调用第5章函数程序代码如下。#include<stdio.h>//递归函数:计算从1到n的所有整数之和intsum(intn){if(n==1){//基准情况
return1;}else{//递归步骤
returnn+sum(n-1);}}intmain(){intn;printf("请输入一个正整数:");scanf("%d",&n);if(n<=0){printf("输入错误:请输入正整数。\n");}else{intresult=sum(n);//调用递归函数
printf("从1到%d的所有整数之和是:%d\n",n,result);}return0;}5.2函数调用5.2.3函数的嵌套与递归调用第5章函数程序运行结果如下。请输入一个正整数:100从1到100的所有整数之和是:5050sum()函数:这个函数接收一个整数n作为参数。如果n等于1,则直接返回1(这是递归的基准情况)。否则,函数返回n加上对sum(n-1)的递归调用的结果。这一步骤将问题规模减小,并朝着基准情况前进。main()函数:首先提示用户输入一个正整数n。检查输入是否为正整数。如果不是,给出错误提示并退出程序。如果输入有效,则调用sum(n)计算从1到n的整数之和,并打印结果。5.2函数调用5.2.3函数的嵌套与递归调用第5章函数【实例5.9】函数的递归调用实现。有5个人,第5个人比第4个人多5个苹果,第4个人比第3个人多5个苹果,第3个人比第2个人多5个苹果,第2个人比第1个人多5个苹果,第1个人有10个苹果,问第5个人有多少个苹果。递推公式如下。5.2函数调用5.2.3函数的嵌套与递归调用第5章函数程序代码如下。#include<stdio.h>//递归函数:计算第n个人有多少个苹果intapples(intn){if(n==1){//基准情况
return10;}else{//递归步骤
returnapples(n-1)+5;}}intmain(){intperson=5;//需要计算第5个人的苹果数量
//调用递归函数并打印结果
intresult=apples(person);printf("第%d个人有%d个苹果。\n",person,result);return0;}程序运行结果如下。第5个人有30个苹果。5.2函数调用5.2.3函数的嵌套与递归调用第5章函数apples()函数:这个函数接收一个整数n作为参数,代表要查询的个人的编号。如果n等于1,则直接返回10(这是递归的基准情况,因为第一个人有10个苹果)。否则,函数返回apples(n-1)+5,这意味着第n个人的苹果数等于第n-1个人的苹果数加上5。main()函数:定义变量person并设置为5,因为想要知道第5个人有多少个苹果。调用apples(person)函数来获取第5个人的苹果数量,并将结果打印出来。5.2函数调用5.2.3函数的嵌套与递归调用第5章函数在C语言中,函数定义的位置是在该函数被调用之前。如果要把函数定义的位置放在调用它的函数的后面,应该在主调函数中进行函数声明。函数声明也称为函数原型,它为编译器提供了关于函数的一些基本信息,包括函数名、返回类型以及参数列表的类型和顺序。函数声明使得编译器能够在遇到函数调用时进行正确的参数配置,检查被调用函数是否正确存在,并确保调用者以正确的方式使用函数。1.函数声明的基本语法函数声明的基本语法格式如下。返回类型函数名(参数类型1参数名1,参数类型2参数名2,...);其中,返回类型:指定函数返回的数据类型。如果函数不返回任何值,则应使用void。函数名:函数的标识符,用于调用该函数。参数列表:一个或多个参数的类型和名称,参数之间用逗号分隔。参数的名称可以省略,仅保留类型,尤其是在函数声明而非定义时。例如:intadd(inta,intb);//声明一个名为add的函数,接受两个int类型的参数,返回一个int类型的结果5.2函数调用5.2.4函数声明第5章函数2.函数声明的位置函数声明通常放置在所有函数定义之前,一般位于文件的顶部,或者在一个单独的头文件中。这样做可以让编译器在解析任何函数调用之前就知道这些函数的存在及其签名(即返回类型和参数列表)。函数声明的位置有以下两种情况。(1)函数声明在主调函数的外部。函数声明在主调函数的外部,此时函数可以被声明之后现出的所有函数调用。例如:#include<stdio.h>//函数声明intadd(inta,intb);intmain(){intsum=add(5,3);//调用add函数
printf("sum:%d\n",sum);return0;}//函数定义intadd(inta,intb){returna+b;//返回两数之和}5.2函数调用5.2.4函数声明第5章函数(2)函数声明在主调函数的内部。函数声明在主调函数的内部,此时函数只能被主调函数调用。例如:#include<stdio.h>intmain(){//函数声明intadd(inta,intb);intsum=add(5,3);//调用add函数
printf("sum:%d\n",sum);return0;}//函数定义intadd(inta,intb){returna+b;//返回两数之和}函数声明是必需的,除非函数定义出现在调用之前。函数声明提供了函数签名的信息,帮助编译器验证函数调用是否合法。在大型项目中,通常将函数声明放在头文件中,而函数定义放在对应的源文件中。这样可以提高代码的可维护性和模块化程度。通过合理使用函数声明,可以程序更加清晰易懂,同时也便于管理和扩展。5.2函数调用5.2.4函数声明第5章函数在C语言中,数组可以作为函数的参数传递给函数。然而,需要注意的是,当数组作为参数传递时,实际上传递的是指向数组第一个元素的地址。这意味着,在函数内部不能直接知道数组的大小,除非另外提供这个信息。1.数组作为函数参数的基本形式数组名代表数组的首地址,数组名作为函数的参数,此时形参和实参所指向的是同一块存储单元,即形参数组可以访问实参数组所在的存储单元,并且还能改变这些单元的内容,从而实参数组元素的值被改变,这就是数组作为参数的真正含意。数组作为函数参数调用函数的语法格式如下。voidfunctionName(dataTypearrayName[],intsize);5.2函数调用5.2.5数组作函数的参数第5章函数2.数组作为函数参数的实例【实例5.10】定义一维数组作函数的参数,并输出一维数组的内容。程序代码如下。#include<stdio.h>//函数声明voidprintArray(intarr[],intsize);intmain(){intnumbers[]={1,2,3,4,5};intsize;size=sizeof(numbers)/sizeof(numbers[0]);//计算数组长度
//调用函数打印数组
printArray(numbers,size);return0;}//函数定义voidprintArray(intarr[],intsize){inti;printf("数组的内容:\n");for(i=0;i<size;++i){printf("%d",arr[i]);}printf("\n");}程序运行结果如下。数组的内容:123455.2函数调用5.2.5数组作函数的参数第5章函数【实例5.11】定义一维数组作为函数参数,并计算5个学生成绩的平均成绩。程序代码如下。#include<stdio.h>//函数声明:计算平均分floatcalculateAverage(intscores[],intlength);intmain(){//定义存放5个学生成绩的数组
inti,scores[5];floatavg;//输入5个学生的成绩
printf("请输入5个学生的成绩(用空格分隔):\n");for(i=0;i<5;i++){scanf("%d",&scores[i]);}//调用函数求平均分
avg=calculateAverage(scores,5);//输出平均成绩
printf("平均成绩为:%.2f\n",avg);return0;}//函数定义:计算平均分floatcalculateAverage(intscores[],intlength){inti,sum=0;for(i=0;i<length;i++){sum+=scores[i];//求和
}return(float)sum/length;//强制类型转换,确保得到浮点数结果}程序运行结果如下。请输入5个学生的成绩(用空格分隔):8590756888平均成绩为:81.205.2函数调用5.2.5数组作函数的参数第5章函数【实例5.12】将一个3×2的二维数组作为函数参数,并在函数中找出该数组中所有元素的最大值。程序代码如下。#include<stdio.h>//函数声明:查找二维数组中的最大值intfindMax(intarr[][2],introws);intmain(){intmax;//定义一个3x2的二维数组
intmatrix[3][2]={{10,25},{7,45},{30,15}};//调用函数求最大值
max=findMax(matrix,3);//输出结果
printf("数组中最大的元素是:%d\n",max);return0;}//函数定义:查找二维数组中的最大值intfindMax(intarr[][2],introws){inti,j,max=arr[0][0];//假设第一个元素为最大值
for(i=0;i<rows;i++){for(j=0;j<2;j++){if(arr[i][j]>max){max=arr[i][j];//更新最大值
}}}returnmax;}程序运行结果如下。数组中最大的元素是:455.2函数调用5.2.5数组作函数的参数第5章函数5.3.1局部变量与全局变量5.3.2变量的存储方式和生存期5.3.3内部函数和外部函数5.3函数变量的作用域和存储类别第5章函数一个程序只包含一个main()函数,变量是在函数的开头处定义的,这些变量在本函数范围内有效,即在本函数开头定义的变量,在本函数中可以被引用。这就引出了一个新的概念,即变量的作用域,也就是说变量在不同的位置定义,其作用域是不一样的,变量的作用域就是变量的有效范围。在C语言中,变量可以根据其定义的位置和作用范围分为局部变量和全局变量。1.局部变量局部变量是在函数内部或复合语句(代码块)内部声明的变量。它们的作用域仅限于声明它们的函数或代码块内部。每次进入声明局部变量的代码块时,系统都会为局部变量分配内存;当退出该代码块时,这些内存会被释放。5.3函数变量的作用域和存储类别5.3.1局部变量与全局变量第5章函数例如,局部变量示例。#include<stdio.h>voidfunction(){inta,b;intlocalVar=10;//局部变量
printf("局部变量的值:%d\n",localVar);}intmain(){inta,b;function();//printf("局部变量的值:%d\n",localVar);//错误!localVar在此处不可见
{intc;c=a+b;}return0;}5.3函数变量的作用域和存储类别5.3.1局部变量与全局变量第5章函数对局部变量应注意如下几点:(1)不同的函数或复合语句中可使用同名的变量,但它们不是同一变量,它们在内存中占不同的单元。例如,function()函数中变量a、b和main()函数中变量a、b,都是相互独立的。(2)主函数main()函数中定义的变量只在主函数中有效。主函数也不能使用其他函数中定义的变量。例如,在main()函数中无法引用function()函数中变量localVar。(3)形参也是局部变量。在函数调用时为其分配内存,退出函数时将释放所占内存。(4)在一个函数内部可在复合语句中定义变量,但变量只在本复合语句中有效。例如,在main()函数中变量c只在本复合语句中有效。(5)编译系统并不检查函数名与局部变量是否同名。例如,下面的用法是正确的。intsum(intn){intsum;}5.3函数变量的作用域和存储类别5.3.1局部变量与全局变量第5章函数2.全局变量全局变量是在所有函数之外声明的变量,这样的变量称为全局变量(也称为外部变量)。它们可以在声明它们的文件内的任何地方访问(包括所有函数),除非被限制在某个特定的作用域内(例如,通过static关键字)。如果一个全局变量未初始化,则它将自动初始化为零(对于数值类型)或空字符(对于字符类型)。全局变量的有效范围为从定义全局变量的位置开始到该源文件结束。若在全局变量定义处之前的函数想引用该全局变量,则需要在该函数中用关键字extern作外部变量声明。【实例5.13】全局变量示例。程序代码如下。#include<stdio.h>intglobalVar=5;//全局变量voidmodifyGlobal(){globalVar+=5;}intmain(){printf("修改前全局变量的值:%d\n",globalVar);modifyGlobal();printf("修改后全局变量的值:%d\n",globalVar);return0;}程序运行结果如下。修改前全局变量的值:5修改后全局变量的值:105.3函数变量的作用域和存储类别5.3.1局部变量与全局变量第5章函数对全局变量应注意如下几点:(1)同一作用域范围内只能定义一次,定义的位置在所有函数之外,系统根据全局变量的定义分配存储单元,若全局变量需要初始化则应在定义时进行。(2)如果全局变量与局部变量同名,则在局部变量的作用范围内,全局变量不起作用(程序对变量的引用遵守最小作用域原则)。(3)由于全局变量可以被多个函数直接引用,因此全局变量为函数间进行数据传递的渠道之一。使用全局变量过多,会降低程序的可读性,人们往往难以清楚地判断出每个外部变量的值。由于在各个函数执行时都可能改变外部变量的值,程序容易出错,因此,要限制使用全局变量。(4)外部变量的声明用于说明该变量是一个已在外部定义过的变量,现要在本函数中使用这个变量,可在不同函数中声明多次。5.3函数变量的作用域和存储类别5.3.1局部变量与全局变量第5章函数【实例5.14】局部变量与全局变量示例。程序代码如下。#include<stdio.h>//全局变量声明intglobalVar=10;//可以被所有函数访问voidLocalAndGlobal(){//局部变量声明
intlocalVar=5;//仅在LocalAndGlobal()函数内有效
printf("在函数内部:\n");printf("全局变量globalVar的值:%d\n",globalVar);printf("局部变量localVar的值:%d\n",localVar);//修改全局变量
globalVar+=5;//修改局部变量
localVar+=2;printf("修改后,在函数内部:\n");printf("全局变量globalVar的值:%d\n",globalVar);printf("局部变量localVar的值:%d\n",localVar);}5.3函数变量的作用域和存储类别5.3.1局部变量与全局变量第5章函数intmain(){printf("在main函数开始时:\n");printf("全局变量globalVar的值:%d\n",globalVar);LocalAndGlobal();//调用函数,该函数会修改全局变量和局部变量
printf("在函数调用之后,main()函数内部:\n");printf("全局变量globalVar的值:%d\n",globalVar);//下面这行代码会导致编译错误,因为localVar只在LocalAndGlobal()函数内部有效
//printf("局部变量localVar的值:%d\n",localVar);return0;}5.3函数变量的作用域和存储类别5.3.1局部变量与全局变量第5章函数程序运行结果如下。在main函数开始时:全局变量globalVar的值:10在函数内部:全局变量globalVar的值:10局部变量localVar的值:5修改后,在函数内部:全局变量globalVar的值:15局部变量localVar的值:7在函数调用之后,main()函数内部:全局变量globalVar的值:155.3函数变量的作用域和存储类别5.3.1局部变量与全局变量第5章函数从变量作用域的角度来观察,变量可以分为局部变量和全局变量,那么在变量定义后是不是直到程序结束都一直有效?答案当然不是,这就引出了一个新的概念:变量的生存期。变量从定义开始分配存储单元,到运行结束存储单元被回收,整个过程称为变量生存期。影响变量生存期的是变量的存储类型,也就是说变量的存储类型不同,其生存期也不同。1.静态存储与动态存储从变量存在的时间(即生存期)来看,有的变量在程序运行的整个过程都是存在的,而有的变量则是在调用期所在的函数时才临时分配存储单元,而在函数调用结束后该存储单元就马上释放了,就是就不存在了。也就是说,变量的存储有两种不同的方式:静态存储方式和动态存储方式。静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式。动态存储方式是指在程序运行期间根据需要进行动态分配的存储空间的方式。静态存储区用于存放全局变量和静态变量;而动态存储区则用于存放函数的形参、自动变量。变量存放在何处决定了变量的生存期。5.3函数变量的作用域和存储类别5.3.2变量的存储方式和生存期第5章函数2.存储类型在C语言中,变量和函数有两个属性,即变量的数据类型,以及变量的存储类型,用变量的存储类型说明来确定变量的存放位置。C语言提供了四种主要的存储类别,它们定义了变量的存储位置、作用域、生存期等属性。(1)自动类型(auto变量)。自动变量存放在数据区的动态存储区中,即函数被调用时,系统为自动变量分配存储单元;一旦函数调用结束,自动变量所占用的存储单元被系统回收。函数中的形参和在函数中定义的变量都属于自动变量。自动变量的定义语法格式如下。auto类型名变量名;定义自动变量时,auto可以省略。省略auto后的格式与先前使用的变量的定义格式相同,所以以前用到的变量都是自动变量。自动变量关键字auto作存储类别的声明。例如:autointa,b=10;与inta,b=10;等价。5.3函数变量的作用域和存储类别5.3.2变量的存储方式和生存期第5章函数【实例5.15】自动变量示例。程序代码如下。#include<stdio.h>voidAutoVariable(){intautoVar=0;//自动变量
printf("autoVar在函数调用前:%d\n",autoVar);autoVar++;printf("autoVar在函数调用后:%d\n",autoVar);}intmain(){AutoVariable();//第一次调用
AutoVariable();//第二次调用
return0;}5.3函数变量的作用域和存储类别5.3.2变量的存储方式和生存期第5章函数程序运行结果如下。autoVar在函数调用前:0autoVar在函数调用后:1autoVar在函数调用前:0autoVar在函数调用后:1(2)静态类型(static变量)。static变量的存储单元被分配在数据区的静态存储区中。如果函数中的局部变量的值在函数调用结束后仍然能保留,便于下一次调用该函数时使用,可以将局部变量定义为static类型。局部变量和全局变量都可以说明为static类型。静态局部变量的生存期与全局变量相同,作用域与局部变量相同。全局变量无论是否被说明为static类型,都将占用静态存储区。静态局部变量可以改变其生存期,但不能改变它的作用域,即静态局部变量不能被其他函数所引用,只是扩大了局部变量的生存期。静态变量的定义语法格式如下。static类型名变量名;5.3函数变量的作用域和存储类别5.3.2变量的存储方式和生存期第5章函数【实例5.16】静态变量示例。程序代码如下。#include<stdio.h>voidStaticVariable(){staticintstaticVar=0;//静态变量
printf("staticVar在函数调用前:%d\n",staticVar);staticVar++;printf("staticVar在函数调用后:%d\n",staticVar);}intmain(){StaticVariable();//第一次调用
StaticVariable();//第二次调用
return0;}程序运行结果如下。staticVar在函数调用前:0staticVar在函数调用后:1staticVar在函数调用前:1staticVar在函数调用后:25.3函数变量的作用域和存储类别5.3.2变量的存储方式和生存期第5章函数【实例5.17】定义静态变量,输出1至10的阶乘值。可以编写一个函数用来进行连乘,如第1次调用时进行1乘以1,第2次调用时再乘以2,第3次调用时再乘以3,依此类推。程序代码如下。#include<stdio.h>intfactorial(intn){staticintf=1;//静态变量,f保留了上次调用结束时的值
f*=n;//在上次的f值的基础上再乘以nreturn(f);//返回值f是n!的值}intmain(){inti;for(i=1;i<=10;i++)//先后调用10次factorial()函数
printf("%d!=%d\n",i,factorial(i));//每次计算并输出i!的值
return0;}5.3函数变量的作用域和存储类别5.3.2变量的存储方式和生存期第5章函数程序运行结果如下。1!=12!=23!=64!=245!=1206!=7207!=50408!=403209!=36288010!=36288005.3函数变量的作用域和存储类别5.3.2变量的存储方式和生存期第5章函数(3)寄存器类型(register变量)。register变量也是自动变量,它与auto型变量的区别在于:register变量的值存放在寄存器中而不是在内存中。寄存器是CPU芯片内部的存储器,访问速度极快。常把一些对运行速度有较高要求、需要频繁引用的变量定义为register类型。因为目前大多数编译器都可以做到程序优化,程序根据优化的结果决定哪些变量是register型变量。因此,现在实际上使用register声明变量的必要性不大,在此不详细介绍它的使用方法和相关规定,读者是只需要知道有这种变量即可,以便在阅读他人写的程序时遇到register时不会感到困惑。寄存器变量的定义语法格式如下。register类型名变量名;(4)外部类型(extern变量)。extern变量即外部变量,是在程序中声明已在函数的外部定义过的全局变量,表明该变量的定义位于其他文件或同一文件的其他位置,并且使用extern声明可以让编译器知道这些变量的存在,有了此声明,即可合法地使用该变量。寄存器变量的定义语法格式如下。extern类型名变量名
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 医疗质量控制中心工作制度(3篇)
- 职业认知能力题目及答案
- AI在职业健康安全技术中的应用
- 2026特种设备安全管理A证全真模拟考试试卷(完整版)
- 2026年北师大版小学数学五年级下册期末综合测试卷及答案
- 网络信息安全基础(AIGC版)随堂前测练习题及参考答案 项目5-任务3-前测练习-单选题5
- CPU设计实践教程-从数字电路到计算机组成 课件 第2章 Minisys实验板介绍
- 2026妇产科医生面试题及答案
- 2026半导体设备行业面试题及答案
- 2026年医疗服务价格项目立项指南考试试题
- 2025江苏省连云港市属国有企业选聘生招录32人笔试历年参考题库附带答案详解
- 2026春青岛版三年级科学下册(全册)各单元知识点复习要点梳理
- 青岛科技大学2026年综合评价招生《笔试 + 面试》模拟试题及参考答案
- GD弹性混凝土无缝连接技术-拼缝180802
- GB/T 17824.3-2026规模猪场环境参数及环境管理技术规范
- 2025浙江金华市武义供销农贸城招聘6人笔试历年常考点试题专练附带答案详解
- GB/T 15000.4-2026标准样品工作导则第4部分:证书、标签和附带文件的内容
- 医疗设备维修保养及应急预案
- 分子诊断设备技师精准操作能力标准
- 工厂搬迁技术方案
- 2025中国热带农业科学院热带生物技术研究所第一批招聘23人笔试试题(第1号)附答案解析
评论
0/150
提交评论