《C语言程序设计》第5章 函数写字字帖_第1页
《C语言程序设计》第5章 函数写字字帖_第2页
《C语言程序设计》第5章 函数写字字帖_第3页
《C语言程序设计》第5章 函数写字字帖_第4页
《C语言程序设计》第5章 函数写字字帖_第5页
已阅读5页,还剩106页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第5章函数学习目标本章主要介绍库函数的使用、函数的定义和调用方法,然后介绍变量的作用域和存储方式,最后讲解内部函数和外部函数的概念。通过本章的学习,需要掌握函数的声明、定义、调用和参数传递,理解函数的递归调用过程,理解变量的作用域和生存期的概念,了解内部函数和外部函数的概念。第5章函数程序设计中也是如此,当程序的功能比较多,如果把所有的程序代码都写在一个主函数(main函数)中,就会使主函数变得繁杂、冗长,使阅读和维护程序变得十分困难,因此我们往往会“生产”一些“部件”来分担主函数的工作,即事先编好一批常用的函数来实现各种不同的功能,这就是模块化的程序设计思路。图5.1是一个程序中函数调用的示意图。第5章函数图5.1函数调用示意图第5章函数例如sqrt函数用来求一个数的平方根,strlen函数用来求一个字符串中字符的个数,把它们保存在函数库中,这些称为库函数。再比如有的程序中要反复实现某一功能(例如打印欢迎信息、操作菜单等),就需要编写能实现这些功能的用户自定义函数,以减少重复编写程序段的工作量。图5.2是从用户角度对函数的分类。第5章函数图5.2从用户角度对函数的分类例5.1输入两个整数,计算它们的和并输出运算结果。#include<stdio.h>voidmain(){

int

add(int

x,inty);//声明add函数

int

a,b,sum;

printf("请输入两个整数:\n");

scanf("%d,%d",&a,&b);sum=add(a,b);//调用add函数printf("两数之和:sum=%d\n",sum);}int

add(int

x,inty)//定义add函数,完成求和运算{

ints;s=x+y;returns;}运行结果:请输入两个整数:8,7两数之和:sum=15例5.2以下关于结构化程序设计的叙述中正确的是(2010年9月全国计算机等级考试二级C试题选择题第11题)A)一个结构化程序必须同时由顺序,分支,循环三种结构组成B)结构化程序使用goto语句会很便捷C)在C语言中,程序的模块化是利用函数实现的D)由三种基本结构构成的程序只能解决小规模的问题分析:程序通过多个函数来实现各种不同的功能,这就是模块化的程序设计思路。正确的是C。例5.3以下选项中关于程序模块化得叙述错误的是(2011年9月全国计算机等级考试二级C试题选择题第12题)A)把程序分成若干相对独立的模块可便于编码和调试B)把程序分成若干相对独立,功能单一的模块,可便于重复使用这些模块C)可采用自底向上、逐步细化的设计方法把若干独立模块组装成所要求的程序D)可采用自顶向下、逐步细化的设计方法把若干独立模块组装成所要求的程序分析:程序模块化的设计方法是自顶而下、逐步细化。所以上述表述中错误的是C。第5章函数5.1库函数5.2函数定义和调用5.3 函数的嵌套调用和递归调用5.4局部变量和全局变量5.5 变量的存储方式和生存期5.6内部函数和外部函数5.1库函数5.1.1标准库函数5.1.2include命令行5.1.1标准库函数它是由系统提供的,用户不必自己定义而直接使用它们,只需在程序前包含有该函数定义的头文件。5.1.1标准库函数必须掌握或熟悉的库函数有:(1)数学函数(头文件math.h):abs()、fabs()、sin()、cos()、tan()、asin()、acos()、atan()、exp()、sqrt()、pow()、fmod()、log()、log10()。(2)字符串处理函数(头文件string.h):strcmp()、strcpy()、strcat()、strlen()。(3)字符处理函数(头文件ctype.h):isalpha()、isdigit()、islower()、isupper()、isspace()。5.1.1标准库函数(4)输入输出函数(头文件stdio.h):getchar()、putchar()、gets()、puts()、fopen()、fclose()、fprintf()、fscanf()、fgetc()、fputc()、fgets()、fputs()、feof()、rewind()、fread()、fwrite()、fseek()。(5)动态存储分配函数(头文件stdlib.h):malloc()、calloc()、realloc()、free()。5.1.2include命令行在程序中调用库函数时,由于库函数并不是由用户在本文件中定义的,而是存放在函数库中的,因此要将调用库函数时所需的信息包含在#include指令指定的“头文件”中。格式:#include<文件名>5.1.2include命令行功能:双引号中的文件名即库函数的文件了,一般存在于编译器目录下面,在计算机中搜索"*.h",就会找到库函数的文件了。将库函数包含到当前的程序内,将指定的文件与当前的源程序组成一个文件。例5.4输出1到10数字的平方根和立方值。#include<stdio.h>#include<math.h>voidmain(){

intx=1;doublesquareroot,power;

while(x<=10){

squareroot=sqrt(x);//调用sqrt平方根函数

power=pow(x,3);//调用pow函数求x的立方

printf("%d的平方根:%3.2f\t%d的立方:%5.0f\n",

x,squareroot,x,power); x++;}}运行结果:1的平方根:1.001的立方:12的平方根:1.412的立方:83的平方根:1.733的立方:274的平方根:2.004的立方:645的平方根:2.245的立方:1256的平方根:2.456的立方:2167的平方根:2.657的立方:3438的平方根:2.838的立方:512 9的平方根:3.009的立方:72910的平方根:3.1610的立方:1000例5.5用程序实现大小写字母转换。#include<stdio.h>#include<ctype.h>voidmain(){ charmsg1,msg2,toupper,tolower;

printf("请输入一个小写字母:"); msg1=getchar();

toupper=toupper(msg1);//调用toupper函数把小写转换为大写

printf("转换为大写:%c\n",toupper);

printf("请输入一个大写字母:"); msg2=getchar();

tolower=tolower(msg2);//调用tolower函数把大写转换为小写

printf("转换为小写:%c\n",tolower);}运行结果:请输入一个小写字母:r转换为大写:R请输入一个大写字母:Q转换为小写:q5.2函数定义和调用5.2.1函数定义的语法5.2.2函数的返回值5.2.3函数调用形式5.2.4函数说明5.2.5调用函数与被调用函数之间的数据传递5.2.1函数定义的语法C语言要求,在程序中使用的函数,必须“先定义,后使用”。程序设计者需要在程序中自己定义想用的而库函数并没有提供的函数,指定函数名字、函数返回值类型、函数实现的功能以及参数的个数与类型,将这些信息通知编译系统。5.2.1函数定义的语法1.定义无参函数无参函数是指函数名后面的括号是空的,没有任何参数。无参函数可以带回或不带回函数值,一般用来执行指定的一组操作。格式:类型名函数名(){ 函数体 }

例如,函数getchar()就是无参函数,它带回的函数返回值就是键盘输入的字符。5.2.1函数定义的语法2.定义有参函数有参函数。在调用函数时,主调函数在调用被调用函数时,通过参数向被调用函数传递数据,一般情况下,执行被调用函数时会得到一个函数值,供主调函数使用。格式:类型名函数名(形式参数表列){ 函数体 }例5.6编写求两个整数较大者的函数。intmax(intx,inty){

intz

;z=x>y

?x

:y

;return(z)

;}分析:这是一个函数定义的例子,intmax(intx,inty)是函数的首部。int是类型名,说明函数返回一个int类型的值;max是函数名,是用户命名的标识符;函数名后面的括号里是形式参数表列(形参),这里有x和y两个,它们的类型是int,注意形参要逐个定义,各形参之间用逗号分开。5.2.2函数的返回值通常,函数调用的目的是使主调函数获得一个确定的值,即函数的返回值。例如,我们在前面介绍的调用语句中有c=max(a,b);这就是调用函数max(a,b),函数执行后返回一个函数值,并把值赋给变量c。5.2.2函数的返回值函数的返回值是通过函数中的return语句获得的。如果需要从被调用函数带回一个函数值,则被调用函数中必须包含return语句。5.2.2函数的返回值对于函数的返回值有如下说明:(1)一个函数中可以有一个以上的return语句,但被执行的只有一个,即返回值只有一个,执行到哪一个return语句,哪一个return语句就起作用。(2)return语句的形式可以是:return表达式;或return(表达式);或return;例如:return(x>y?x:y);(3)函数返回值的类型,应当是定义函数时指定的函数类型。例如函数定义为:floatadd(float

x,floaty){return(x+y);}函数类型为float,则通过return语句返回的值类型也应为float。如果函数类型和return语句中表达式的值类型不一致,则以函数类型为准。(4)对于不带回值的函数,定义函数为“void类型”。此时在函数体中不得出现return语句。例5.7以下关于return语句的叙述中正确的是(2010年3月全国计算机等级考试二级C试题选择题第24题)A)一个自定义函数中必须有一条return语句B)一个自定义函数中可以根据不同情况设置多条return语句C)定义成void类型的函数中可以有带返回值的return语句D)没有return语句的自定义函数在执行结束时不能返回到调用处分析:按照return语句的特点,正确的是B。5.2.3函数调用形式定义函数的目的是为了调用此函数,以实现所需的操作。1.函数调用函数调用的一般形式:格式:函数名(实际参数表)例如:m=getchar();//调用无参函数

c=max(a,b);//调用有参函数2.函数调用的方式按函数调用在程序中出现的形式和位置来分,可以用以下几种方式调用函数:(1)函数表达式函数调用出现在另一个表达式中,用函数的返回值参与表达式的运算。这种方式要求函数是有返回值的。例如:c=max(a,b);这是一个赋值表达式,把函数max(a,b)的返回值赋予变量c。(2)函数调用语句函数调用的一般形式加上分号即构成单独的一个语句。例如:printf(“%d”,a);scanf(“%d”,&b);(3)函数参数函数调用作为另一个函数的实参。例如:m=max(a,max(b,c));其中max(b,c)是一次函数调用,它的值作为max()另一次调用的实参。经过赋值后,m的值是a,b,c三者最大者。例5.8有以下程序(2009年9月全国计算机等级考试二级C试题选择题第24题)#include<stdio.h>voidfun(intp){ intd=2; p=d++; printf("%d",p);}main(){ inta=1; fun(a);printf("%d\n",a); }程序运行后的输出结果是:A)32 B)12 C)21 D)22分析:main()调用fun(),将实参a的值,拷贝一份传递给形参p,因此p也等于1;fun()函数体内执行了p=d++,自增运算符在d的后面,先取值后加1,则p=2,d=3,第一个打印结果为2;函数调用结束,形参不影响实参的值,因此实参a保持原值,第2个打印结果为1,选C。例5.9有以下函数(2011年3月全国计算机等级考试二级C试题填空题第9题)voidprt(char

ch,intn){inti;

for(i=1;i<=n;i++)

printf(i%6!=0?"%c":"%c\n",ch);}执行调用语句prt('*',24);后,函数共输出了

行*号。分析:调用语句prt('*',24);传递参数,ch值为'*',n值为24,所以for循环从1到24。而printf语句其实是一个条件语句,首先判断i%6!=0,如果i的值不能被6整除,则相当于执行printf("%c",ch);如果i的值能被6整除,则相当于执行printf("%c\n",ch);那么,从1~24,只有4次被6整除,所以共换行4次,即函数共输出了

4行*号。5.2.4函数说明一般情况下,在使用用户自定义函数,并且被调函数与主调函数在同一个程序文件中时,应在主调函数中对被调函数作函数说明。函数说明的作用是向编译系统声明将要调用此函数,并将有关信息通知编译系统。函数说明的一般形式:格式:类型名函数名(形式参数表列);例如:intadd(intx,inty);或者

int

add(int,int)

;以上两种形式都可。第二种形式省略了参数名,因为编译系统不检查参数名,所以参数名可有可无。例5.10输入两个整数,输出其中较大者。要求用函数来实现。#include<stdio.h>voidmain(){intmax(intx,inty);//在main中对max函数说明

inta,b,c;printf("请输入两个整数

:");scanf("%d,%d",&a,&b);c=max(a,b);//调用max函数

printf("较大的数是

:%d\n",c);}intmax(intx,inty)//定义max函数{intz; z=x>y?x:y; returnz;}运行结果:请输入两个整数

:7,8较大的数是

:8C语言在以下几种情况时,可以省略函数说明:(1)当被调函数的函数定义出现在主调函数之前时,在主调函数中可以不对被调函数进行说明而直接调用。(2)如果在所有函数定义之前,即文件开头,已对各个函数进行了说明(称为外部声明),则在后面的主调函数中,可不再对被调函数作说明。(3)对库函数的调用不需要作说明,只需把该函数的头文件用include命令包含在源文件前面。5.2.5调用函数与被调用函数之间的数据传递函数参数分为形式参数和实际参数,函数定义时出现的是形式参数。当主调函数调用有参函数时,调用语句中出现的是实际参数。形式参数和实际参数的功能是作数据传递。函数调用时,主调函数把实际参数的值传送给被调函数的形式参数,从而实现主调函数向被调函数的数据传递。实际参数与形式参数个数应相等,类型应一致。传递参数时,实际参数与形式参数按顺序对应,一一传递数据。例5.11有以下程序(2009年3月全国计算机等级考试二级C试题选择题第24题)#include<stdio.h>int

f(int

x,inty){return((y-x)*x);}main(){inta=3,b=4,c=5,d;d=f(f(a,b),f(a,c));

printf("%d\n",d);}程序运行后的输出结果是:A)10 B)9 C)8 D)7分析:main()调用f函数,实参是函数f(a,b)和f(a,c)的返回值,所以首先求f(a,b)和f(a,c)的函数值,f(a,b)又是将a,b作为实参传递给函数形参,返回函数值3;同样,f(a,c)返回函数值6;继续计算函数f(3,6),获得函数返回值9,赋值给变量d。选B。例5.12有以下程序(2011年3月全国计算机等级考试二级C试题选择题第33题)#include<stdio.h>int

fun(int

x,inty){if(x!=y)return((x+y)/2);elsereturn(x);}main(){

inta=4,b=5,c=6; printf("%d\n",fun(2*a,fun(b,c)));}程序运行后的输出结果是:A)3 B)6 C)8 D)12分析:要输出fun(2*a,fun(b,c))的值,首先求fun(b,c)的函数值,b、c作为实参传递给函数形参,返回函数值5,注意fun函数的类型定义为int,所以返回值取整;继续计算函数fun(8,5),获得函数返回值6。选B。例5.13有以下程序(2011年9月全国计算机等级考试二级C试题选择题第24题)#include<stdio.h>doublef(doublex);main(){doublea=0;inti;for(i=0;i<30;i+=10)a+=f((double)i);printf("%5.0f\n",a);}doublef(doublex){returnx*x+1;}程序运行后的输出结果是:A)503B)401C)500D)1404分析:本题通过for循环3次调用f函数。第1次调用i=0,函数返回1,则a=1;第2次调用i=10,函数返回101,则a=102;第3次调用i=20,函数返回401,则a=503;循环条件已不成立,所以循环结束。选A。5.3 函数的嵌套调用和递归调用5.3.1 函数的嵌套调用5.3.2函数的递归调用5.3.1 函数的嵌套调用C语言函数的定义是互相独立、互相平行的,函数之间不存在从属关系。一个函数内不能定义另一个函数,即不能嵌套定义,但可以嵌套调用函数,也就是说,在调用一个函数的过程中,又调用另一个函数。函数的嵌套调用过程如图5.3所示。main()函数f1()函数f2()函数调用f1()函数调用f2()函数结束返回返回①②③④⑤⑥⑦⑧⑨图5.3函数的嵌套调用图5.3中所示的是两层嵌套,其执行过程是:(1)执行main函数的开头部分。(2)在main中调用f1函数,程序转到f1函数。(3)执行f1函数过程中,调用f2函数,程序转到f2函数。(4)在f2函数执行完后,程序返回到f1函数中调用f2函数的位置。(5)继续执行f1函数中未执行的部分,执行完后返回到main函数调用f1函数的位置。(6)继续执行main函数直到程序结束。例5.14输入3个整数,找出其中最大的数。用函数的嵌套调用来处理。#include<stdio.h>voidmain(){

intmax3(inta,int

b,intc);//对max3的函数声明

int

a,b,c,max;

printf("请输入3个整数:");

scanf("%d%d%d",&a,&b,&c); max=max3(a,b,c);//调用max3函数,找出3个数中最大数

printf("最大的数是:%d\n",max);}intmax3(inta,int

b,intc)//定义max3函数{

intmax2(inta,intb);//对max2的函数声明

intm; m=max2(a,b);//调用max2函数,找出a、b中较大者,放在m中

m=max2(m,c);//调用max2函数,找出m、c中较大者,即3个数中最大者

return(m);}intmax2(inta,intb)//定义max2函数{

if(a>=b) returna;//若a较大,则返回a else returnb;//否则返回b}运行结果:请输入3个整数

:785最大的数是

:8分析:从代码可以看到,程序由主函数、max3函数、max2函数组成。主函数调用max3函数,max3函数调用max2函数,形成嵌套调用。5.3.2函数的递归调用在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。C语言允许函数的递归调用。在调用函数f的过程中,又要调用f函数,这是直接递归调用,见图5.4。如果在调用f1函数过程中又要调用f2函数,而在调用f2函数过程中又要调用f1函数,就是间接递归调用,见图5.5。5.3.2函数的递归调用f函数调用f函数f1函数调用f2函数f2函数调用f1函数

图5.4直接递归调用

图5.5间接递归调用

5.3.2函数的递归调用可以看到,无论是直接递归调用还是间接递归调用,都会形成无终止的自身调用,这在程序中是不允许的。为了结束递归调用,可以用if语句来控制,只有在某一条件成立时才继续执行递归调用,否则就不再继续。5.3.2函数的递归调用例5.15从键盘输入一个正整数n,用递归方法求n!。分析:由于n!=n*(n-1)!,所以要计算n!,就必须先计算(n-1)!,而要知道(n-1)!,必须先计算(n-2)!,以此类推,要求2!,必须先知道1!,而1!=1。以上关系可用下面的递归公式表示:n!=n!=1(n=0,1)n*(n-1)!(n>1)程序代码如下:#include<stdio.h>voidmain(){

int

fac(intn);//fac函数声明

intn;

inty;

printf("请输入一个正整数:");

scanf("%d",&n);//输入要求阶乘的数

y=fac(n);

printf("%d!=%d\n",n,y);}int

fac(intn)//定义fac函数{

intf;

if(n<0)//n不能小于0

printf("n<0,错误!"); elseif(n==0||n==1)//n=0或1时,n!=1,结束递归

f=1; elsef=fac(n-1)*n;//n>1时,n!=n*(n-1)! return(f);}运行结果:请输入一个正整数:55!=120例5.16设有如下函数定义(2011年3月全国计算机等级考试二级C试题选择题第32题)int

fun(intk){if(k<1)return0;elseif(k==1)return1;elsereturnfun(k-1)+1;}若执行调用语句:n=fun(3),则函数fun总共被调用的次数是A)2 B)3 C)4 D)5分析:这是一个函数递归调用的例题。当执行fun(3)时,形参k的值为3,执行函数语句,由于不满足k<1或k==1,则递归调用fun(2),同样的情况,再次递归调用fun(1),这时返回函数值1,所以总共调用fun函数3次。选B。例5.17有以下程序(2011年9月全国计算机等级考试二级C试题填空题第11题)#include<stdio.h>voidfun(intx){if(x/5>0)fun(x/5);

printf("%d",x);}main(){fun(11);printf("\n");}程序运行后的输出结果是

。分析:这也是函数递归调用的程序。当执行fun(11)时,因为11/5=2>0,所以递归调用fun(2),这时不再满足if条件,则执行printf语句,输出2;接着回到上一层调用点,继续后面语句的执行,输出11,所以程序运行的结果是2_11。这里2_11之间的短横表示输出时的空格。printf()函数是格式输出函数,输出时应注意它的格式要求。5.4局部变量和全局变量5.4.1 局部变量5.4.2全局变量在C语言中,一个变量能够起作用(被引用)的程序范围,称为变量的作用域,即一个变量定义之后,在何处能够使用该变量。若变量在整个程序范围内的各个函数中均可被访问,称其为全局变量。如果变量仅在特定的函数或程序块内可被访问,不能被其他函数或程序访问,称其为局部变量。5.4.1 局部变量上面提过,变量的作用域决定于变量的定义位置。在一个函数内部定义的变量只在本函数范围内有效,在此函数以外是不能使用这些变量的;在复合语句内定义的变量只在本复合语句范围内有效,只有在本复合语句内才能引用它们。这些都是局部变量。

例如:floatf1(inta)//a,b,c在f1函数中有效{int

b,c;...}intf2(intx,inty)//x,y,i,j在f2函数中有效{int

i,j

;{

intc;//c在复合语句中定义,只在此语句范围内有效

c=i+j;}...}voidmain()//m,n在main函数中有效{int

m,n;...}5.4.2全局变量全局变量也称为外部变量,它是在函数之外定义的变量。全局变量不属于某一个特定的函数,它可以为本文件中其他函数所共用,它的有效范围为从定义变量的位置开始到本源文件结束。5.4.2全局变量int

a,b;//定义外部变量a,b,全程序有效floatf1()//定义f1函数{...}floatx,y;//定义外部变量x,y,从定义到程序结束都有效intf2(){

inti;//i在f2函数中定义,是局部变量

...}voidmain()//定义main函数{...}5.4.2全局变量设置全局变量的作用是增加函数间数据联系。由于全局变量是各个函数都能引用的变量,因此如果在一个函数中改变了全局变量的值,就能影响到其他函数中全局变量的值,相当于各个函数间有直接的传递通道。例5.18输入正方体的长宽高l,w,h,求其体积即3个面的面积。#include<stdio.h>ints1,s2,s3;//定义全局变量s1,s2,s3int

vs(int

a,int

b,intc)//定义求体积及面积的函数vs{

intv; v=a*b*c; s1=a*b;//函数可访问全局变量

s2=b*c;//为s1,s2,s3赋值

s3=a*c; returnv;//返回体积值}voidmain(){

int

v,l,w,h;

printf("请输入正方体的长宽高:");

scanf("%d%d%d",&l,&w,&h); v=vs(l,w,h);//调用vs函数求体积、面积

printf("正方体的体积v=%d,各面面积s1=%d,s2=%d,s3=%d\n",v,s1,s2,s3);}运行结果:请输入正方体的长宽高:345正方体的体积v=60,各面面积s1=12,s2=20,s3=15分析:本例通过函数来计算正方体的体积及3个面的面积,但函数只能返回一个值,所以把3个面积定义为全局变量,这样一般函数和主函数都可以访问,我们在函数中计算出面积值,赋给全局变量,主函数也访问这些变量来获取它们的值。这也反映出全局变量是实现函数之间数据通讯的有效方法。例5.19分析下面程序。#include<stdio.h>inta=3,b=5;//定义全局变量a,bvoidmain(){

int

add(int

a,intb);//函数声明

inta=8;//定义局部变量a

printf("两数之和:%d\n",add(a,b));//调用add函数,a,b为实参}int

add(int

x,inty){ return(x+y);//求两数之和}运行结果:两数之和:13分析:在本例中,既定义了全局变量a、b,也定义了同名局部变量a。局部变量a是在主函数中定义的,其作用域为主函数,在此范围内,全局变量a就失去作用,所以当调用函数add(a,b)时,传递的实参a是局部变量a,而不是全局变量a。所以输出结果为13。例5.20有以下程序(2009年9月全国计算机等级考试二级C试题填空题第11题)#include<stdio.h>inta=5;voidfun(intb){inta=10;a+=b;printf("%d",a);}main(){intc=20;fun(c);a+=c;printf("%d\n",a);}程序运行后的输出结果是

。分析:程序首先定义了全局变量a=5。当执行主函数main时,会调用fun函数,fun函数中定义了局部变量a=10,则全局变量a失去作用,所以输出30。返回到主函数调用点继续执行,主函数中的a是全局变量a,所以输出25,因此程序运行的结果是3025。5.5 变量的存储方式和生存期5.5.1 动态存储方式与静态存储方式5.5.2 局部变量的存储类别5.5.3 全局变量的存储类别5.5.4 存储类别小结5.5.1 动态存储方式与静态存储方式在C语言中,变量不仅有确定的数据类型定义,还有存储方式的定义。变量的存储方式是其存储属性,即变量在内存中的存储方法,不同的存储方法,将影响变量值存在的时间(即生存期)。变量的生存期是指程序在运行期间,从给变量分配存储单元,到所分配的存储单元被系统收回的那段时间。有的变量在程序运行的整个过程都是存在的,始终占有固定的存储单元;而有的变量则是在调用其所在的函数时才临时分配存储单元,而在函数调用结束后该存储单元就马上释放了,变量也就不存在了在C语言中,变量的存储有两种方式:静态存储方式和动态存储方式。静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式,而动态存储方式则是在程序运行期间根据需要进行动态的分配存储空间的方式。内存中提供给程序的存储空间通常分为3部分,如图5.6所示。程序区静态存储区动态存储区图5.6程序占用的内存空间程序的数据分别存放在静态存储区和动态存储区中。全局变量全部存放在静态存储区中,在程序开始执行时就给全局变量分配存储区,程序执行完毕即释放。在程序执行过程中它们占据固定的存储单元,而不是动态进行分配和释放。在动态存储区中则存放以下数据:(1)函数形式参数。在调用函数时给形参分配存储空间。(2)函数调用时的现场保护和返回地址等。以上数据在函数调用开始时分配动态存储空间,函数结束时释放这些空间。注意在两次调用同一函数时,每次分配给局部变量的存储空间不一定相同。在定义和声明变量时,一般应同时指定其数据类型和存储类别。如果不指明存储类别,表示采用默认方式(即系统隐含指定的某一种存储类别)。所以变量的完整定义形式为:格式:存储类别数据类型变量名;C的存储类别包括4种:自动(auto)、静态(static)、寄存器(register)、外部(extern)。不同的存储类别,将影响到变量的生存期以及作用域。5.5.2 局部变量的存储类别1.自动变量(auto)在函数内或复合语句中定义的变量,如果不指定其存储类别为static,那么就是自动存储变量,其关键字为auto。自动存储变量都是动态地分配存储空间的,数据存储在动态存储区中,即调用函数时,系统会给这些变量分配存储空间,在函数调用结束时就自动释放这些存储空间。

例如:intfun(){autointnum;//定义num为自动变量

…}实际上,我们在定义这类局部变量时,往往省略其存储类别,因为关键字“auto”可以省略,不写auto则隐含指定为自动存储类别。所以以上函数定义可写作:intfun(){

intnum;//定义num为自动变量

…}2.静态局部变量(static)有时需要函数中局部变量的值在函数调用结束后不消失,即其占用的存储单元在整个程序运行期间都不释放,那么可以将该局部变量定义为静态局部变量,用关键字static进行声明。例如:staticint

m,n;例5.21静态局部变量与自动局部变量的比较。#include<stdio.h>voidmain(){ voidtest();//test函数声明

int

i,a=0;//自动局部变量a,在主函数中定义

for(i=0;i<3;i++) test();//调用test() {

inta=10;//自动局部变量a,在复合语句中定义

a=a*10;

printf("复合语句中:a=%d\n",a); } a=a+10;

printf("主函数中:a=%d\n",a);}voidtest(){ staticinta=5;//静态局部变量a,赋初值一次

printf("静态变量:a=%d\n",a); a=a+10;}运行结果:静态变量:a=5静态变量:a=15静态变量:a=25复合语句中:a=100主函数中:a=10分析:首先在主函数中循环调用test()函数,函数中的静态局部变量a,在每次函数调用结束后,它的值并不消失,所以循环调用时其值能够保持连续性。复合语句和主函数中定义了同名自动局部变量a,注意其不同的作用域。例5.22若函数中有定义语句:intk;则(2009年3月全国计算机等级考试二级C试题选择题第12题)A)系统将自动给k赋初值0B)这时k中的值无定义C)系统将自动给k赋初值-1D)这时k中无任何值分析:函数中定义intk;则k应是自动存储变量,它不同于静态存储变量,系统不会自动给k赋值,所以这时k中的值无定义。选B。例5.23有以下程序(2011年3月全国计算机等级考试二级C试题选择题第34题)#include<stdio.h>intfun(){staticintx=1;x*=2;returnx;}main(){int

i,s=1;for(i=1;i<=3;i++)s*=fun();printf("%d\n",s);}程序运行后的输出结果是:A)0B)10C)30D)64分析:主函数中通过循环,共3次调用fun()函数。在fun()函数中,定义了静态局部变量x=1。第一次调用fun(),返回x值为2;第二次调用fun(),由于静态变量保留上一次函数调用的结果,所以乘以2后返回x值为4;第三次调用fun(),返回x值为8,所以在主函数中,s=2*4*8=64。选D。例5.24有以下程序(2009年9月全国计算机等级考试二级C试题选择题第34题)#include<stdio.h>

int

f(intn);main(){inta=3,s;

s=f(a);s=s+f(a);printf("%d\n",s);}

int

f(intn){staticinta=1;n+=a++;returnn;}

程序运行以后的输出结果是:

A)7B)8C)9D)10分析:在函数f()中定义了静态变量a,第1次调用f函数,返回4,同时在函数中静态变量a的值自增为2;第2次调用f函数,静态变量会保留上一次的结果,所以返回5,则s=9。选C。例5.25有以下程序(2011年9月全国计算机等级考试二级C试题选择题第33题)#include<stdio.h>int

f(intm){staticintn=0; n+=m; returnn;}main(){intn=0;

printf("%d,",f(++n));printf("%d\n",f(n++));}程序运行后的输出结果是:A)1,2B)1,1 C)2,3 D)3,3分析:主函数中定义n=0,调用函数f(++n),即f(1),在函数f中定义静态变量n,加1后返回1;主函数再次调用f(n++),++运算符在变量的后面,所以还是f(1),函数中的静态变量保留上一次的值,所以在原值1的基础上加1,返回2,最终输出1,2。选A。3.寄存器变量(register)一般情况下,变量值是存放在内存中的,但我们也知道,计算机的CPU内部包含若干个通用寄存器,硬件在对数据操作时,常常是先把数据取到寄存器(或一部分取到寄存器),然后进行操作。而且,计算机对寄存器中数据的操作速度要远远快于对内存中数据的操作速度。为提高程序的运行速度,可将使用十分频繁的局部变量说明为寄存器变量,将其存储在CPU的寄存器中。寄存器变量定义的关键字是register,例如:registerintn;5.5.3 全局变量的存储类别全局变量(即外部变量)是在函数的外部定义的,它的作用域从变量定义处开始,到本程序文件的末尾。全局变量都存放在静态存储区中,占有固定的存储单元,生存期为程序的整个运行过程。1.在一个文件内声明外部变量如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件结束。如果想在定义点之前引用该外部变量,解决的方法是在引用之前用关键字extern对该变量作“外部变量声明”。表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。例5.26求两数中较大者。#include<stdio.h>voidmain(){

int

max(int

x,inty);//函数声明externintA,B;//全局变量声明printf("两数中较大的是:%d\n",max(A,B));//使用全局变量作为实参}intmax(intx,inty)//函数定义{intz;z=x>y?x:y;return(z);}intA=15,B=-8;//定义全局变量A,B运行结果:两数中较大的是:15分析:在本程序文件的最后定义了外部变量A、B,其作用域是从定义处开始的,那么在主函数main中不能引用外部变量A、B。现在main函数中用extern对A和B进行“外部变量声明”,所以从“声明”处起,可以合法地使用该外部变量A和B。用extern声明外部变量时,类型名可以写也可以不写。例如,“externintA,B;”也可以写成“externA,B;”。2.在多文件的程序内声明外部变量一个C程序可以由一个或多个源程序文件组成。如果程序由多个源程序文件组成,那么在一个文件中想引用另一个文件中已定义的外部变量,有什么办法呢?实现的方法是:在其中一个文件中定义外部变量,而在另一文件中用extern对这个变量作“外部变量声明”。在编译和连接时,编译系统会由此知道这个变量是一个已在别处定义的外部变量,并将在另一文件中定义的外部变量的作用域扩展到本文件,在本文件中就可以合法地引用该外部变量。例5.27输入A和m,求Am的值。文件file1.c:#include<stdio.h>intA;//定义外部变量voidmain(){

int

power(int);//函数声明

int

b,m;

printf("请输入一个整数和它的方次:");

scanf("%d%d",&A,&m); b=power(m);

printf("%d**%d=%d\n",A,m,b);}文件file2.c:externA;//在file2中声明file1定义的外部变量A,使其在本文件有效int

power(intn){

int

i,y=1;

for(i=1;i<=n;i++) y*=A; return(y);}运行结果:请输入一个整数和它的方次:323**2=93.用static声明外部变量有时在程序

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论