版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1模块化程序设计C/C++语言程序设计案例教程函数2023/10/725.1C程序结构5.2函数的定义5.3函数调用和函数说明5.4函数的嵌套调用和递归调用5.5变量的作用域和存储方式5.6函数间的数据传递5.7指针函数5.8函数指针要求掌握函数的概念和定义方法,理解实参和形参的一致性(5.1---5.4)理解函数的执行过程,理解函数中的各种数据的作用域和传递方法(5.4-5.6)了解函数指针和指针函数的概念(5.7-5.8)学习内容
学习目标
C语言是一种结构化程序设计语言,函数是其基本模块,当要解决的问题比较复杂时,可以把复杂问题分解成若干个简单问题,每个简单问题用单独的函数实现,通过函数调用执行某个功能,如此将一个复杂的程序分化,可使程序的结构更为清晰。5.1C程序结构2023/10/74①程序结构清晰,可读性好。②减少重复编码的工作量。③可多人共同编制一个大程序,缩短程序设计周期,提高程序设计和调试的效率。使用函数的好处……C程序源程序文件n函数1函数m……源程序文件1函数1函数n复杂问题的结构化程序设计思想:“自上而下,逐步细化,结构化,模块化”C程序的一般结构案例一四则运算器1.问题描述计算器是一种很方便的小工具。参照计算器进行简单模拟,实现针对两个整数的四则运算。2.问题分析本案例需要实现加、减、乘、除四则运算,其中加、减、乘三种运算处理方法完全一致,除法因要考虑除数不能为0的情况,略有不同。因此此处以乘法操作为例,对计算过程进行分析。执行乘法操作的细节如下:(1)乘法操作需要两个操作数,首先由用户输入一个数据,作为第一个操作数;(2)其次用户输入一个操作符,此处应输入乘法符号;(3)然后用户输入第二个操作数;(4)最后用户按下回车符,将数据传入计算机内进行计算,计算器操作之后输出结果。除法运算与乘法运算也基本相同,只是在输入第二个操作数时,需要进行判断,当第二个操作数不为0时才能继续往下执行。3. C语言代码#include<stdio.h>voidAdd(floatop1,floatop2) /*加法函数*/{floats;s=op1+op2;printf("%.2f\n",s);}voidSub(floatop1,floatop2) /*减法函数*/{floats;s=op1-op2;printf("%.2f\n",s);}voidMult(floatop1,floatop2) /*乘法函数*/{floats;s=op1*op2;printf("%.2f\n",s);}voidDiv(floatop1,floatop2) /*除法函数*/{if(op2==0)printf("除数不能为0!");else{floats; /*复合语句中定义的块变量*/s=op1/op2;printf("%.2f\n",s);}}voidmain(){floatop1,op2; /*定义两个操作数变量*/charch; /*定义一个运算符*/printf("请输入数据和四则运算符(+-*/),如:2+4\n");scanf("%f%c%f",&op1,&ch,&op2);switch(ch){case'+':Add(op1,op2);break;
case'-':Sub(op1,op2);break;case'*':Mult(op1,op2);break;case'/':Div(op1,op2);break;default:break;}}4.程序运行结果请输入数据和四则运算符(+-*/),如:2+43*618.005.1函数的基本概念和操作5.1.1函数的概念C语言是结构化程序设计语言,一个C语言程序由一个或多个源程序文件组成。这样可以分别编写、分别编译,提高调度效率。一个源程序文件由一个或多个函数组成。一个源程序文件是一个编译单位。C语言中的函数是一个独立完成某种功能的程序块,其中封装了一些程序代码和数据。使用者只需关心函数的功能和使用方法,而不必关心函数功能的具体实现细节。利用函数可将复杂问题的解决过程分割成一个个小的模块,每一个模块编写一个函数,而各函数分别完成一个功能单一而独立的任务,因此C语言程序通常是由许多函数组成。函数在使用之前除了标准函数库的函数以外,其他函数都必须事先定义。在C语言中,根据使用的角度不同,函数可以有以下的分类。(1)从用户使用的角度,函数分为两类:标准函数和用户自定义函数。标准函数:在C语言的编译系统中提供了很多系统预定义的函数,用户程序只需包含有相应的头文件就可以直接调用,不同的编译系统提供的库函数名称和功能是不完全相同的。例如在上一章所介绍的字符串处理函数都是系统给我们提供的标准函数,只需要在使用时将头文件 "string.h" 包含进来就可以了。用户自定义函数:用户根据自己特殊需要,按照C语言的语法规定编写一段程序,实现特定的功能。(2)从函数参数的形式,函数分为两类:无参函数和有参函数。无参函数:使用该类函数时,不需给函数提供数据信息,就可以直接使用该函数提供的功能。有参函数:使用该类函数时,必须给该函数提供所需要的数据信息,按照提供的数据不同,在使用该函数后获得不同的结果。5.1.2函数的定义
前面提到,从函数参数的形式角度来看,函数分为无参函数和有参函数,下面分别介绍这两种函数的定义形式。1.无参函数的定义无参函数定义的一般形式为返回值类型函数名()
{
说明部分执行部分}说明:(1)无参函数定义由函数头部和函数体两部分组成。函数头部包括返回值类型,函数名两个部分;在 {} 内的部分称为函数体,其在语法上是一个复合语句。(2)函数名是唯一标识函数的名字,是C语言中任何合法的标识符,而且在该标识符后面必须有一对圆括号,用来表明该标识符为函数名。(3)返回值类型是在调用函数结束后,函数给调用者返回结果所具有的类型,返回值的类型为各种基本数据类型和自定义数据类型;函数在被调用后也可以没有返回值,此时返回值类型为void;另外,函数默认返回值类型为int。例如:voidoutput(){printf("*****");}注意:(1)函数体内可以是0条、1条或多条语句。当函数体是0条语句时,称该函数为空函数。空函数作为一种什么都不执行的函数,通常在程序设计初期作为临时函数使用,在设计过程中再实现或扩充功能。注意函数体内无论有多少条语句,大括号是不能省略的。例如:voidnothing(){}(2)在函数体内不能定义另一个函数,也就是说函数定义不能嵌套。例如下面函数的定义是错误的。voidoutput1(){printf("*****");voidoutput2(){printf("#####");}}2.有参函数的定义有参函数定义与无参函数的区别在于有参函数带有参数表列,作用是在函数被调用时接受提供给该函数的数据,以便在函数体内进行处理。有参函数定义的一般形式为返回值类型函数名(参数表列)
{
说明部分执行部分}说明:(1)返回值、函数名和函数体与无参数含义相同;(2)参数表列通常称为形式参数表(简称形参表),形式参数表的形式为类型参数名1,类型参数名2,...,类型参数名n其中,形参表说明函数参数的名称、类型和数目,由一个或多个参数说明组成,每个参数说明之间用逗号分隔。书写函数时要养成给函数注释的习惯,一般最少要对函数的功能,参数的意义进行说明。例如,输出两个整数值的函数可定义为:voidoutint(intx,inty) /*有参函数定义*/{printf("x=%d,y=%d",x,y);}3.函数的返回值与return语句调用者在调用函数时,函数有时需要把处理的结果返回给调用者,这个结果就是函数的返回值,函数的返回值是由return语句传递的。return语句的形式:return(表达式);或return表达式;或return;注意:(1) return语句中表达式的类型应与函数返回值类型一致,若不一致,则以函数返回值的类型为准,对于数值型数据将自动进行类型转换。void类型函数中return后不得跟表达式。(2)一个函数中可以有多个return语句,函数在碰到第一个return语句返回,函数返回值为第一个return语句中表达式的值;若return后面无表达式,则返回调用函数处。(3)若函数体内没有return语句,就一直执行到函数体的末尾后返回调用函数。这时会返回一个不确定的函数值,若确实不要求返回函数值,则应将函数定义为void类型。例如,求两个整数的最大者的函数可定义为:intmax(intx,inty){if(x>y)returnx;elsereturny;}5.1.3函数的调用与函数说明函数在定义之后并不能主动运行,必须通过对函数调用才能实现函数的功能。一个函数可以被其他函数多次调用(main函数不能被任何函数调用),调用函数的函数称为主调函数,被调用的函数称为被调函数。如果被调函数是有参函数,主调函数在调用时将数据传递给被调函数,从而得到所需要的处理结果。1.函数调用的形式(1)无参函数调用形式为函数名();(2)有参函数调用形式为函数名(参数表);注意:(1)函数调用语句中函数名与函数定义的名字相同。(2)有参函数调用时参数表中列出的参数是实际参数(简称实参)。实参的形式为参数1,参数2,…,参数n其中,各参数间用逗号隔开,实参与形参要保持顺序一致、个数一致、类型应一致。实参与形参按顺序一一对应,传递数据。当实参与形参类型不一致时,实参的值转化为形参的类型赋给形参。(3)实参要有确定的值,它可以是一个表达式或者是值。例如:inty=3;output(); /*无参函数调用*/outint(2,y); /*有参函数调用*/2.函数调用的使用方式(1)函数调用作为一个语句,函数完成一定功能。例如:putchar('c');(2)函数调用出现在表达式中,这时要求被调函数必须带有返回值,返回值将参加表达式的运算。例如求两个整数的最大者的函数intmax(intx,inty)可以有如下调用:m=2+max(5,6);(3)函数调用作为函数的实参。例如Max=max(a,max(b,c));3.函数说明在函数调用过程中,若被调函数(除函数返回值类型为int、char之外)的定义出现在主调函数之后,则在调用函数前还必须对该被调函数进行原型说明。函数类型说明的格式为返回值类型函数名(参数类型表);说明:(1)圆括号是函数的标志,不能省略,如果省略,就成为一般变量的说明了。(2)参数类型表的形式与函数定义的形参表相同,也可以只列出形参的类型名。(3)函数类型说明语句应放在主调函数函数体中的数据说明位置或在主调函数前面。例5.1定义一个函数,suv函数功能为求两个浮点数的之和,并在主函数中调用此函数。#include"stdio.h"voidmain(){floatsuv(float,float);/*对suv函数进行说明*/floatx1,x2,x3;printf("inputx1,x2");scanf("%f%f",&x1,&x2);x3=suv(x1,x2);printf("\nsuv=%6.2f",x3);}floatsuv(floatx,floaty){printf("%f,%f",x,y);
return(x+y);}注意:(1)如果被调函数定义出现在主调函数定义之前时,在主调函数中不必对被调函数进行类型说明。(2)当被调函数的返回值为int型或char型时,在主调函数中不必对被调函数进行原型说明,但是有些编译器(例如VisualC、BolandC)要求即使被调函数的返回值为int型或char型时,也要提前声明,所以尽量把被调用函数写在主调函数之前。4.函数调用的执行过程调用函数的过程分为如下几步:第一步,将实参的值赋给形参。实参和形参的关系如同赋值表达式的右操作数与左操作数的关系,对于基本类型的参数,如果实参的类型与形参的类型相同,则实参直接赋值给形参;否则实参按形参的类型执行类型转换后再赋给形参。如果实参是数组名,因为数组名表示数组的起始地址,所以实参传递的是数组的起始地址,而不是变量的值;第二步,将程序执行流程从主调函数的调用语句转到被调函数的定义部分,执行被调函数的函数体;第三步,当执行到被调函数函数体的第一个return语句或者最右边的一个大花括号时,程序执行流程返回到主调函数的调用语句。若调用语句是表达式的一部分,则应用函数的返回值参与表达式运算之后继续向下执行;若调用语句是单独一条语句则直接继续向下执行。案例二阶乘之和1.问题描述编写程序,计算s=1!+2!+…+n!,其中n的值由键盘输入。求和与求阶乘分别用两个函数来完成。2.问题分析本案例首先可以定义两个子函数:子函数sum()实现求和功能,子函数fact()实现求阶乘功能;然后通过主函数调用子函数sum(),子函数sum()调用子函数fact(),实现函数的嵌套调用。3. C语言代码#include<stdio.h>longfact(intn) /*定义子函数fact求阶乘*/{if(n==0||n==1)return1;elsereturnn*fact(n-1);}longsum(intn) /*定义子函数sum求累加和*/{longs=0;for(inti=1;i<=n;i++)s=s+fact(i); /*嵌套调用*/returns;}voidmain(){intn;printf("请输入累加的项数:");scanf("%d",&n);printf("1!+2!+…+%d!=%ld\n",n,sum(n)); /*输出调用函数sum之后的和值*/}4.程序运行结果请输入累加的项数:41!+2!+…+4!=335.2函数的嵌套调用和递归调用
在函数的调用中,经常会用到两类特殊的调用形式,它们分别是函数的嵌套调用和递归调用。5.2.1函数的嵌套调用
1.函数嵌套调用的定义函数定义部分不能嵌套,各个函数定义是相对独立的,但是任何函数内部都可以调用另外一个非主函数main()的函数。这样一个函数调用另一个函数,而另一个函数又可以调用其他的函数的调用过程,就形成了函数的嵌套调用。2.函数嵌套调用的过程例如程序中有两个子函数,分别是f1和f2,主函数main对f1函数进行调用,f1函数又对f2函数进行调用,这种情况成为函数的嵌套调用,调用过程描述如图5.1所示。说明:要实现函数的嵌套调用,需要满足以下条件:(1)至少有两个子函数;(2)子函数的函数体中有对其他子函数的调用语句。图5.1函数的嵌套调用过程5.2.2函数的递归调用
如果要用递归思想求解这个问题,就需要学习函数递归调用的相关知识。1.函数递归调用的定义在调用一个函数的过程中如果出现直接或间接调用函数自身,即任何一个函数中调用自身的过程称为函数的递归调用。C语言的特点之一就在于允许函数递归调用。2.函数递归调用的过程根据调用的形式,函数递归调用可以分为直接调用和间接调用,其执行过程如图5.2和图5.3所示。图5.2直接调用图5.3间接调用从图5.2可以看出,在调用f函数的过程中又调用f函数,这种过程是直接递归调用。从图5.3可以看出,在调用f1函数的过程中要调用f2函数,而在调用f2函数过程中又要调用f1函数,从而形成了间接递归调用自身的函数调用过程。说明:从递归函数的执行过程我们可以看到以上两种递归调用都是永远无法结束的自身调用,程序中不应存在这种无终止的递归调用,而只应出现有限次数的,有终止的递归调用。解决方法是可利用if语句来控制这样循环调用自身的过程,将递归调用过程改为当某一条件成立时才执行递归调用,否则就不再执行递归调用过程。例5.2歌德巴赫猜想:一个充分大的偶数(大于或等于6)可以分解为两个素数之和。编写程序,将6至50之间全部偶数表示为两个素数之和。分析:根据问题描述,要将6至50之间的偶数分解为两个整数,然后判断分解出的两个整数是否均为素数。若是,则满足题意,否则应重新进行分解和判断。#include<stdio.h>#include<stdlib.h>#include<math.h>intisSushu(inti){if(i<2)return0; /*最小素数是2*/for(intj=2;j<=sqrt(i);j++){if(i%j==0)return0; /*如果不是素数返回0*/}return1; /*如果是素数返回1*/} void_func(intnum){for(inti=3;i<50;i++){if(isSushu(i)) /*如果i是素数*/{if(isSushu(num-i)) /*如果num-i也是素数*/
{printf("%2d=%2d+%2d",num,i,(num-i));break;}}}}voidmain(){intnum;intn=0;for(num=6;num<=50;num+=2){_func(num);/*将num传递给函数_func()*/n++;if(n%5==0) /*每行5个换行*/printf("\n");}}程序运行结果如下:6=3+38=3+510=3+712=5+714=3+1116=3+1318=5+1320=3+1722=3+1924=5+1926=3+2328=5+2330=7+2332=3+2934=3+3136=5+3138=7+3140=3+3742=5+3744=3+4146=3+4348=5+4350=3+47案例三
值传递与地址传递1.问题描述分析下面两个程序的执行结果有什么不同。程序1:#include<stdio.h>voidchange(intx,inty)/*交换x和y的值*/{inttemp;temp=x;x=y;
y=temp;printf("x=%d,y=%d\n",x,y);}voidmain(){inta,b;printf("inputa,b:");
scanf("%d,%d",&a,&b);printf("a=%d,b=%d\n",a,b);change(a,b);printf("a=%d,b=%d\n",a,b);}程序2:#include<stdio.h>voidchange(int*x,int*y)/*交换x和y两个指针指向的值*/{inttemp;temp=*x;*x=*y;
*y=temp;printf("x=%d,y=%d\n",*x,*y);}voidmain(){inta,b,*m,*n;printf("inputa,b:");
scanf("%d,%d",&a,&b);printf("a=%d,b=%d\n",a,b);m=&a;n=&b;change(m,n);printf("a=%d,b=%d\n",a,b);}2.问题分析要解决本例问题,需要我们对下面几个问题进行分析:(1)子函数的形参可以是什么类型;(2)当形参的类型为数值和为指针时有什么不同;(3)当形参是指针类型时,实参应该如何给形参。3.执行结果程序1:inputa,b:2,3a=2,b=3x=3,y=2a=2,b=3程序2:inputa,b:2,3a=2,b=3x=3,y=2a=3,b=25.3函数间数据传递在函数调用时由主调函数将实参的值传送给被调函数的形参,或者由被调函数向主调函数返回数据的过程都称为函数间的数据传递。按照实参传递值的类型(即实参存储的是值还是指针),函数间数据传递分为两种方式:传值方式和传递地址方式。5.3.1传值方式传递数据
当实参为简单类型变量或者数组元素时,实参的值在函数调用时被传递给形参,但形参的值在函数返回时不能传递给实参。因为在内存中,实参与形参是不同的存储单元,在调用函数时给形参分配存储单元,并将实参对应的值赋值给形参,由于形参是被调函数中的局部动态变量,调用结束后形参被释放,实参仍然保留并维持原值。所以执行被调用函数时,即使形参的值发生改变,但并不能够改变主调函数实参的值。分析案例三中程序1,函数change()中形参x和y为简单类型变量,所以当主函数调用它时参数传递方式为值传递方式,所以在调用函数change()时,系统首先为形参x和y分配相应的存储单元,然后将实参a、b的值传递给x、y,当执行change()的函数体部分后,x和y的值互换。函数执行完毕,形参x和y的存储单元被释放,其值消失,我们看到主函数中实参a和b的存储单元仍保留原先的值。程序执行过程中实参与形参的变化过程如图5.6~5.8所示:(1)主函数调用change()函数(执行语句change(a,b))。图5.6调用change()函数(2)程序流程转到change()函数执行。图5.7参数值传递(3) change()函数调用结束,返回主函数。图5.8change()函数调用结束5.3.2传地址方式传递数据如果实参的值是指针类型,也就是一个数据的内存地址,在将实参的值传递给形参时,被调函数形参所接受是这个数据的内存地址,则在函数内可以通过地址改变实参所指向的数据,这种传递数据的方式称为传地址方式。分析案例三中程序2,这里函数change()的形参为指针变量,在调用此函数时参数传递属于地址传递方式,所以调用函数change()时传给函数形参的是指向主函数中变量a、b的指针,即变量a、b的地址,所以函数change()中的形参x和y分别存储主函数中的变量a、b地址的指针。这样在函数change()中,就可以利用形参通过指向运算来改变主函数中变量a、b的值。程序执行过程中实参与形参的变化过程如图5.9~5.11所示:(1)主函数调用函数change()(执行语句change(a,b))。图5.9调用change()函数(2)程序流程转到函数change()执行。步骤1:在函数change()中通过“*”运算访问主函数中实参所指向的变量a、b;图5.10地址传递步骤2:在函数change()中交换主函数中实参所指向的变量a、b的值。图5.11形参改变实参的值数组名作函数参数时,把实参数组a的起始地址传递给形参数组b,这样两个数组就共占同一段内存单元。形参数组中各元素值的变化同样会使实参数组元素值发生变化。如果形参是数组形式,则实参可以是数组名或指向数组的指针,如果实参是数组名,则形参也可以是同样类型的数组名或指针。通过这个例子我们可以看出:如果我们需要改变某个变量的值,不能传递变量的值而要传递变量的地址,即使这个变量是指针类型,也要传递指针的地址,也就是多级指针。注意设计形参时要和实参类型匹配,使用类型一致的地址,才能改变实参的值。案例五远水不救近火
1.问题描述“远水不救近火”意思是远处的水救不了近处的火,这是因为起火的地方已经超出了水的作用范围。假如有一火警,需要用水扑灭,有远处的水,也有近处的水,如果选择远处的水,则火警无法解除,如果选择近处的水,则火警解除。2.问题分析在C语言中,不同的变量也有这不同的作用范围。将C语言中的变量比作“水”和“火”,那么定义在不同代码中的变量也有远近之分。本案例要求实现代码中不同位置变量的定义和使用。要实现上述案例,需要考虑如下几个问题:(1)什么是变量的作用域;(2)变量的作用域如何设置;(3)如何操作不同作用域的变量。3. C语言代码#include<stdio.h>intwater=1; /*全局的"水",是全局变量*/voidFfire(intfire) /*扑火*/{intwater=1; /*局部的"水"属于远水*/fire-=water;}voidmsg(intfire) /*"火"是否被扑灭?*/{if(fire==0)printf("火被扑灭啦!\n");elseprintf("警报尚未解除!\n");}voidmain(){intfire=1; /*主函数中的"火",局部变量*/ Ffire(fire); /*①扑火*/printf("远水救近火?");msg(fire);{ intwater=1; /*块变量*/intfire=1; /*块变量*/fire-=water; /*②扑火*/printf("近水救近火?");msg(fire);}msg(fire);fire-=water; /*③扑火,fire是局部变量*/msg(fire);}4.程序运行结果远水救近火?警报尚未解除!近水救近火?火被扑灭啦!警报尚未解除!火被扑灭啦!5.4变量的作用域与存储方式
5.4.1变量的作用域根据变量的定义的位置,变量可分为局部变量和全局变量。局部变量和全局变量在内存中存储位置不同,作用范围也有差异。要解决上述问题,需要学习变量作用域的相关知识。1.局部变量在函数内部定义的变量、形参及复合语句块中定义的变量都称为局部变量。局部变量只在定义它的函数内或复合语句内有效,其他的函数或程序块不能对它进行存取操作。因此在不同函数内定义的局部变量可以同名,它们代表的对象不同,互不影响。voidf1(floata,floatb,floatc){…}floatf2(floatx,intn){inti,j;…}voidmain(){inti,j;…f1(i,i,j);…for(i=0;i<10;i++){floatx,y;…}…f2(i,j);}形参a、b、c的作用范围形参x、n,变量i、j的作用范围变量x、y的作用范围变量i、j的作用范围分析下面程序中变量的作用范围。在函数f2和主函数中都定义变量i、j,但是因为它们所在作用域范围不同,所以虽然变量名相同,实际上是不同的变量。主函数中所定义的变量x、y位于复合语句中,它的作用范围只在复合语句内有效。关于局部变量有如下几点说明:(1)主函数main中定义的变量只在主函数中有效,在其他函数中无效;(2)函数中的形参在函数头中,也是局部变量,只在本函数内有效;(3)在一个函数内部复合语句中可以定义变量,这些变量只在本复合语句中有效,称为块变量;(4)不同的函数内部可以定义相同名字的变量,它们名字虽然相同,但代表的对象却不同,为它们分配的存储单元也不同。2.全局变量全局变量又称为外部变量,是在函数外部定义的变量。其作用范围是从变量定义的位置开始到本源文件结束为止。或有extern说明的本程序的其他源文件。floatx,y;voidf1(intm){floatp;…}intk1,k2;floatf2(intm,intn){inti,j;…}voidmain(){…}全局变量k1、k2的作用范围全局变量x、y的作用范围
分析下面全局变量的作用域范围。floatx,y;voidf1(intm){floatp;…}intk1,k2;floatf2(intm,intn){inti,j;…}voidmain(){…}全局变量k1、k2的作用范围全局变量x、y的作用范围全局变量的使用说明:(1)尽量限制全局变量的使用。这是因为全局变量在程序运行整个过程中自始至终占用存储单元,不利于内存空间的动态分配,另外使用全局变量也降低了函数或模块的通用性和程序的通用性、可靠性。例如在函数或模块中使用全局变量,则执行时会直接依赖于全局变量,这时若将一个函数移到另一个文件中,就必须把涉及的所有全局变量一起移过去,这给程序的修改和维护都带来不便。最后,过多地使用全局变量也会降低程序的清晰性,使人难以确定每个时刻使用各个全局变量的值,因为在程序执行时各个函数都可能改变全局变量的值,使程序很容易出错。(2)全局变量的定义与说明。全局变量同局部变量一样,也遵循先定义后使用的原则。注意每个全局变量只能定义一次,否则编译程序时将出错,而且最好定义在使用它的所有函数之前。如果在全局变量定义之前的函数要使用全局变量,只能对这个全局变量进行说明,而不能再次定义。(3)同一个源文件中局部变量与全局变量可以同名,在局部变量的作用范围内,全局变量被屏蔽不起作用。同理,在块变量起作用的范围内,同名的局部变量和全局变量也会被屏蔽。例5.3同名的全局变量与局部变量。#include"stdio.h"intx=1; /*全局变量x*/main(){printf("x=%d\n",x); /*输出全局变量x的值*/{intx=10; /*局部块变量x*/printf("x=%d\n",x);/*输出局部块变量x的值,全局变量与局部变量同名,全局变量被屏蔽*/}printf("x=%d\n",x); /*输出全局变量x的值*/}程序运行结果如下:x=1x=10x=15.4.2变量的存储类别
在C语言程序运行时占用的存储空间通常分为3个部分:程序区、静态存储区和动态存储区。程序区中存放的是程序执行时的机器指令,数据分别存放在静态存储区和动态存储区中。数据存储可分为静态存储和动态存储方式,静态存储方式就是程序运行期间为变量分配固定的存储空间,变量存储在静态存储区,而动态存储方式是程序运行期间根据需要为变量动态分配存储空间,变量存储在动态存储区。在C语言中每一个变量和函数都有两个属性:数据类型和存储类别,数据类型我们在前面已经介绍过。存储类别分为两大类:静态存储类别和动态存储类别,具体包括4种:自动(auto)、静态(static)、寄存器register)和外部(extern)。1.局部变量的存储方式局部变量因其存储类别不同,可能放在静态存储区,也可能放在动态存储区。1)自动局部变量(简称自动变量)用关键字auto作存储类型说明,存储在动态存储区。当局部变量未指明存储类别时,默认为auto存储类别。int
f(int
a) /*形参a默认为自动变量*/{
auto
int
b,c=9; /*定义局部变量b、c为自动变量*/
…}a、b、c为自动变量,执行完f函数后自动释放其所占的存储单元。2)静态局部变量用关键字static作存储类型说明,存储在静态存储区,在程序运行期间占据一个永久性的存储单元,即使在退出函数后,存储空间仍旧存在,直到源程序运行结束为止,静态变量的初始化在编译时进行,程序运行时不再进行初始化操作。注意形参不允许说明为静态存储类别。例5.4分析下面程序运行结果。程序a:#include"stdio.h"f1(){intx=0;/*定义局部变量x为自动变量*/
x++;
printf("x=%d\n",x);}main(){f1();f1();f1();}程序运行结果如下:x=1x=1
x=1f1函数中自动变量x在函数结束时会被释放,当再次调用函数时需要进行重新定义,即执行intx=0;语句。所以三次调用x的值都为1。程序b:
#include"stdio.h"f1(){staticintx=0;/*定义局部变量x为静态变量*/x++;printf("x=%d\n",x);}main(){f1();f1();f1();}
程序运行结果如下:x=1x=2
x=3因为静态变量存储在静态存储区,直到程序运行结束后才被释放,所以静态变量的初始化语句只能被执行一次。在f1函数中将x说明成静态变量,x只在编译阶段初始化一次,初值为0。f1函数第一次被调用时,调用结束后值为1;第二次调用时x的初值是上次调用结束后x值,因此输出x值为2;同样第三次调用时,x的值为3。案例六查找子串
1.问题描述编写一个指针函数search(s,t),函数功能为在字符串s中查找一个任意子串t,如果找到,则返回字符子串在s中第一次出现的起始位置,否则返回NULL。2.问题分析本案例要定义的函数要求返回一个地址,即它的返回值是一个指针类型,要实现上述案例,需要考虑如下几个问题:(1)什么样的函数能够返回指针类型;(2)返回值类型是指针类型的函数如何定义;(3)为什么使用返回值类型是指针类型的函数?调用时应注意什么问题?3. C语言代码#include<stdio.h>char*search(char*s,char*t) /*s指向原字符串,t指向待查找的子串*/{char*ps=s,*pt,*pc; /*ps指向s*/while(*ps!='\0') /*ps中还有字符没有比较*/{/*pt指向t,pt中当前被检查的字符不是'\0',且*pt(子串的字符)与ps中的字符(*pc)相等*/for(pt=t,pc=ps;*pt!='\0'&&*pt==*pc;pt++,pc++);/*如果t中所有字符与s中被比较的字符都相等,则返回t在s中的位置(即ps的当前值);如果*pt等于'\0'(t中字符已检查完),或者*pt与*pc不相等,则跳出当前内循环,将字符串s的指针移向下一个字符,然后t从头开始继续与s中的字符进行比较,直至s中已无字符可比结束,则返回0(t不在字符串s中)*/if(*pt=='\0') /**pt=='\0'意味着查找成功,返回当前查找的起点*/return(ps);ps++;}returnNULL;}voidmain(){charstr1[50],str2[50],*s;puts("请输入源字符串");gets(str1); puts("请输入查找的子串");gets(str2);s=search(str1,str2);if(s!=NULL)printf("子串在主串中下标为:%d\n",s-str1);/*输出子串t在s中出现的位置*/elseprintf("notfound\n");}4.程序运行结果请输入源字符串abcdefg请输入查找的子串cd子串在主串中下标为:25.5指针函数与函数指针5.5.1指针函数一个函数被调用后返回的值可以是整型、实型或字符型等类型,也可以是一个指针类型。当一个函数的返回值为指针类型时,我们就称这个函数是一个返回指针的函数,简称指针函数。1.指针函数的定义指针函数的一般定义形式为存储类型数据类型*函数名(参数表列)
{
函数体}其中,存储类型与一般函数相同,分为extern型和static型;“数据类型*”是指函数的返回值类型是指针类型,数据类型说明指针所指向的目标变量的数据类型。例如:
staticfloat*a(intx,inty);函数a为静态有参函数,返回值是一个指向float变量的指针。与一般函数的定义相比较,指针函数在定义时注意如下两点:(1)在函数名前面要加上一个“*”号,表示该函数的返回值是指针型的;(2)在函数体内必须有return语句,其后跟随的表达式结果值必须是指针类型。2.指针函数的说明如果函数定义在后,调用在前,则在调用前应对其进行说明。一般说明的形式为数据类型*函数名(参数类型表);例如,上述函数a的定义部分放在主调函数之后,对函数a说明如下:float*a(int,int);案例七函数指针实现四则运算1.问题描述编写一个程序,在该程序中包括一个函数func,该函数可以根据传递给它的函数指针来实现对两个数的加、减和乘法运算。2.问题分析本案例完成的重点是用函数指针作为func的参数,可以按如下步骤完成:(1)定义三个函数分别完成两个数的加、减和乘法运算;(2)定义函数func,形参分别为一个函数指针与两个参与运算的数;(3)在主函数中调用函数func,实参分别为要做运算的函数名和两个运算数。在这个程序中,主函数中主要调用了func函数。func函数的功
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 政府采购询价采购制度
- 商贸企业采购合同管理制度
- 采购监督管理制度
- 程序文件采购管理制度
- 幼儿园食品采购索证票制度
- 设备招标采购制度
- 后勤采购流程管理制度
- 招标采购内控制度
- 采购部门发票管理制度
- 采购部门评估制度
- 工程标杆管理办法细则
- 尿源性脓毒血症的护理
- 光电信息工程相关课件
- 殡仪馆司机管理制度
- 绿色船舶拆除-绿色船舶拆除技术
- 马工程西方经济学(精要本第三版)教案
- 香港公司劳动合同协议
- 【初中 语文】第15课《青春之光》课件-2024-2025学年统编版语文七年级下册
- 2024年海南省烟草专卖局招聘考试真题
- GenAI教育在不同场景下的应用案例分析与演进路径
- 大连重工:中企华评报字(2024)第5436号资产评估报告
评论
0/150
提交评论