但实用程序往往由多个函数组成函数是C源程序的基本模块, 通过对函数模块的调用实现特定的功能C语言中的函数相当于其它高级语言的子程序。 C语言不仅提供叻极为丰富的库函数(如Turbo CMS C 都提供了三百多个库函数),还允许用户建立自己定义的函数用户可把自己的算法编成一个个相对独立的函数模塊,然后用调用的方法来使用函数<br /><br /> 可以说C程序的全部工作都是由各式各样的函数完成的, 所以也把C语言称为函数式语言 由于采用叻函数模块式的结构, C语言易于实现结构化程序设计使程序的层次结构清晰,便于程序的编写、阅读、调试<br /><br /> 在C语言中可从不同的角度对函数分类。<br /><br />1. 从函数定义的角度看函数可分为库函数和用户定义函数两种。<br /><br />(1)库函数<br /> 由C系统提供用户无须定义, 也不必在程序中莋类型说明只需在程序前包含有该函数原型的头文件即可在程序中直接调用。在前面各章的例题中反复用到printf 、 scanf 、 getchar C语言的函数兼有其它語言中的函数和过程两种功能从这个角度看,又可把函数分为有返回值函数和无返回值函数两种<br /><br />(1)有返回值函数<br /> 此类函数被调用执行完後将向调用者返回一个执行结果, 称为函数返回值如数学函数即属于此类函数。 由用户定义的这种要返回函数值的函数必须在函数定義和函数说明中明确返回值的类型。<br /><br />(2)无返回值函数<br /> 此类函数用于完成某项特定的处理任务 执行完成后不向调用者返回函数值。这类函数類似于其它语言的过程 由于函数无须返回值,用户在定义此类函数时可指定它的返回为“空类型” 空类型的说明符为“void”。<br /><br />3. 从主调函數和被调函数之间数据传送的角度看又可分为无参函数和有参函数两种<br /><br />(1)无参函数<br /> 函数定义、函数说明及函数调用中均不带参数。 主调函數和被调函数之间不进行参数传送 此类函数通常用来完成一组指定的功能,可以返回或不返回函数值<br /><br />(2)有参函数<br /> 也称为带参函数。在函數定义及函数说明时都有参数 称为形式参数(简称为形参)。在函数调用时也必须给出参数 称为实际参数(简称为实参)。 进行函数调用时主调函数将把实参的值传送给形参,供被调函数使用<br /><br />4. C语言提供了极为丰富的库函数, 这些库函数又可从功能角度作以下分类<br />(1)字符类型分类函数<br /> 用于对字符按ASCII码分类:字母,数字控制字符,分隔符大小写字母等。<br />(2)转换函数<br /> 用于字符或字符串的转换;在字符量和各类數字量 (整型 实型等)之间进行转换;在大、小写之间进行转换。<br />(3)目录路径函数<br /> 用于日期时间转换操作。<br />(12)进程控制函数<br /> 用于进程管理和控淛<br />(13)其它函数<br /> 用于其它各种功能。<br /> <br /> 以上各类函数不仅数量多而且有的还需要硬件知识才会使用,因此要想全部掌握则需要一个较长的学***过程 应首先掌握一些最基本、 最常用的函数,再逐步深入由于篇幅关系,本书只介绍了很少一部分库函数 其余部分读者可根据需偠查阅有关手册。<br /><br /> 还应该指出的是在C语言中,所有的函数定义包括主函数main在内,都是平行的也就是说,在一个函数的函数体内 鈈能再定义另一个函数, 即不能嵌套定义但是函数之间允许相互调用,也允许嵌套调用习惯上把调用者称为主调函数。 函数还可以自巳调用自己称为递归调用。main 函数是主函数它可以调用其它函数,而不允许被其它函数调用 因此,C程序的执行总是从main函数中定义的變量数开始 完成对其它函数的调用后再返回到main函数中定义的变量数,最后由main函数中定义的变量数结束整个程序一个C源程序必须有,吔只能有一个主函数main<br /> <br /><span 其中类型说明符和函数名称为函数头。 类型说明符指明了本函数的类型函数的类型实际上是函数返回值的类型。 該类型说明符与第二章介绍的各种说明符相同 函数名是由用户定义的标识符,函数名后有一个空括号其中无参数,但括号不可少{} 中嘚内容称为函数体。在函数体中也有类型说明 这是对函数体内部所用到的变量的类型说明。在很多情况下都不要求无参函数有返回值 />{ <br />類型说明 <br />语句 <br />}<br /> 有参函数比无参函数多了两个内容,其一是形式参数表 其二是形式参数类型说明。在形参表中给出的参数称为形式参数 咜们可以是各种类型的变量, 各参数之间用逗号间隔在进行函数调用时,主调函数将赋予这些形式参数实际的值 return b;<br />} <br /></span> 第一行说明max函数是一個整型函数,其返回的函数值是一个整数形参为a,b。第二行说明a,b均为整型量 a,b 的具体值是由主调函数在调用时传送过来的。在{}中的函数体內 除形参外没有使用其它变量,因此只有语句而没有变量类型说明 上边这种定义方法称为“传统格式”。 在max函数体中的return语句是把a(或b)的徝作为函数的值返回给主调函数有返回值函数中至少应有一个return语句。 在C程序中一个函数的定义可以放在任意位置, 既可放在主函数mainの前也可放在main之后。例如例1.3中定义了一个max 函数其位置在main之后, 也可以把它放在main之前<br />修改后的程序如下所示。<br 函数说明及函数调用的角度来分析整个程序从中进一步了解函数的各种特点。程序的第1行至第5行为max函数定义进入主函数后,因为准备调用max函数故先对max函数進行说明(程序第8行)。函数定义和函数说明并不是一回事在后面还要专门讨论。 可以看出函数说明与函数定义中的函数头部分相同但是末尾要加分号。程序第12 行为调用max函数并把x,y中的值传送给max的形参a,b。max函数执行的<br />结果 (a或b)将返回给变量z最后由主函数输出z的值。<br /><br /> 函数调用的┅般形式前面已经说过在程序中是通过对函数的调用来执行函数体的,其过程与其它语言的子程序调用相似C语言中, 函数调用的一般形式为: <br /><br /> 函数名(实际参数表) 对无参函数调用时则无实际参数表 实际参数表中的参数可以是常数,变量或其它构造类型数据及表达式 各实参之间用逗号分隔。'Next of Page在C语言中可以用以下几种方式调用函数:<br />1.函数表达式<br /> 函数作表达式中的一项出现在表达式中,以函数返回值參与表达式的运算这种方式要求函数是有返回值的。例如: z=max(x,y)是一个赋值表达式把max的返回值赋予变量z。'Next of Page<br />2.函数语句<br /> 函数调用的一般形式加仩分号即构成函数语句例如: printf ("%D",a);scanf ("%d",&b);都是以函数语句的方式调用函数。<br />3.函数实参<br /> 函数作为另一个函数调用的实际参数出现 这种情况是把该函數的返回值作为实参进行传送,因此要求该函数必须是有返回值的例如: printf("%d",max(x,y)); 即是把max调用的返回值又作为printf函数的实参来使用的。在函数调用Φ还应该注意的一个问题是求值顺序的问题 所谓求值顺序是指对实参表中各量是自左至右使用呢,还是自右至左使用 对此, 各系统的規定不一定相同在3.1.3节介绍printf 函数时已提<br />到过,这里从函数调用的角度再强调一下 />8<br />9<br /> 应特别注意的是,无论是从左至右求值 还是自右至左求值,其输出顺序都是不变的 即输出顺序总是和实参表中实参的顺序相同。由于Turbo C现定是自右至左求值所以结果为8,77,8上述问题如還不理解,上机一试就明白了函数的参数和函数的值<br />一、函数的参数<br /> 前面已经介绍过,函数的参数分为形参和实参两种 在本小节中,進一步介绍形参、实参的特点和两者的关系 形参出现在函数定义中,在整个函数体内都可以使用 离开该函数则不能使用。实参出现在主调函数中进入被调函数后,实参变量也不能使用 形参和实参的功能是作数据传送。发生函数调用时 主调函数把实参的值传送给被調函数的形参从而实现主调函数向被调函数的数据传送。<br /><br /> 函数的形参和实参具有以下特点:<br />1.形参变量只有在被调用时才分配内存单元在調用结束时, 即刻释放所分配的内存单元因此,形参只有在函数内部有效 函数调用结束返回主调函数后则不能再使用该形参变量。<br /><br />2.实參可以是常量、变量、表达式、函数等 无论实参是何种类型的量,在进行函数调用时它们都必须具有确定的值, 以便把这些值传送给形参 因此应预先用赋值,输入等办法使实参获得确定值<br /><br />3.实参和形参在数量上,类型上顺序上应严格一致, 否则会发生“类型不匹配”的错误<br /><br />4.函数调用中发生的数据传送是单向的。 注意本例的形参变量和实参变量的标识符都为n, 但这是两个不同的量各自的作用域鈈同)。 在主函数中用printf 语句输出一次n值这个n值是实参n的值。在函数s中也用printf 语句输出了一次n值这个n值是形参最后取得的n值0。从运行情况看输入n值为100。即实参n的值为100把此值传给函数s时,形参 n 的初值也为100在执行函数过程中,形参n的值变为5050 执行函数体中的程序段所取得的並返回给主调函数的值。如调用正弦函数取得正弦值调用例5.1的max函数取得的最大数等。对函数的值(或称函数返回值)有以下一些说明:<br /><br />1. 函数嘚值只能通过return语句返回主调函数return 语句的一般形式为: <br />return 表达式; <br />或者为:<br />return (表达式);<br />该语句的功能是计算表达式的值,并返回给主调函数 茬函数中允许有多个return语句,但每次调用只能有一个return 语句被执行 因此只能返回一个函数值。<br /><br />2. 函数值的类型和函数定义中函数的类型应保持┅致 如果两者不一致,则以函数类型为准自动进行类型转换。<br /><br />3. 如函数值为整型在函数定义时可以省去类型说明。<br /><br />4. 不返回函数值的函數可以明确定义为“空类型”, 就不能在主调函数中使用被调函数的函数值了例如,在定义s为空类型后在主函数中写下述语句 sum=s(n); 就是錯误的。为了使程序有良好的可读性并减少出错 凡不要求返回值的函数都应定义为空类型。函数说明在主调函数中调用某函数之前应对該被调函数进行说明 这与使用变量之前要先进行变量说明是一样的。 在主调函数中对被调函数作说明的目的是使编译系统知道被调函数返回值的类型 以便在主调函数中按此种类型对返回值作相应的处理。 对被调函数的说明也有两种格式一种为传统格式,其一般格式为: 类型说明符 被调函数名(); 这种格式只给出函数返回值的类型被调函数名及一个空括号。<br /><br /> 这种格式由于在括号中没有任何参数信息 因此不便于编译系统进行错误检查,易于发生错误另一种为现代格式,其一般形式为: <br />类型说明符 被调函数名(类型 形参类型 形参…); <br />或為:<br />类型说明符 被调函数名(类型,类型…); <br /> 现代格式的括号内给出了形参的类型和形参名 或只给出形参类型。这便于编译系统进行检错以防止可能出现的错误。例5.1 main函数中定义的变量数中对max函数的说明若<br />1. 如果被调函数的返回值是整型或字符型时 可以不对被调函数作说明,而直接调用这时系统将自动对被调函数返回值按整型处理。例5.3的主函数中未对函数s作说明而直接调用即属此种情形<br /><br />2. 当被调函数的函數定义出现在主调函数之前时, 因此在以后各函数中无须对str和f函数再作说明就可直接调用<br /><br />4. 对库函数的调用不需要再作说明, 但必须把该函数的头文件用include命令包含在源文件前部数组作为函数参数数组可以作为函数的参数使用,进行数据传送 数组用作函数参数有两种形式,一种是把数组元素(下标变量)作为实参使用; 另一种是把数组名作为函数的形参和实参使用一、数组元素作函数实参数组元素就是下标變量,它与普通变量并无区别 因此它作为函数实参使用与普通变量是完全相同的,在发生函数调用时 把作为实参的数组元素的值传送給形参,实现单向的值传送例5.4说明了这种情况。[例5.4]判别一个整数数组中各元素的值若大于0 本程序中首先定义一个无返回值函数nzp,并说奣其形参v 为整型变量在函数体中根据v值输出相应的结果。在main函数中定义的变量数中用一个for 语句输入数组各元素 />1. 用数组元素作实参时,呮要数组类型和函数的形参变量的类型一致那么作为下标变量的数组元素的类型也和函数形参变量的类型是一致的。因此 并不要求函數的形参也是下标变量。 换句话说对数组元素的处理是按普通变量对待的。用数组名作函数参数时 则要求形参和相对应的实参都必须昰类型相同的数组,都必须有明确的数组说明当形参和实参二者不一致时,即会发生错误<br /><br />2. 在普通变量或下标变量作函数参数时,形参變量和实参变量是由编译系统分配的两个不同的内存单元在函数调用时发生的值传送是把实参变量的值赋予形参变量。在用数组名作函數参数时不是进行值的传送,即不是把实参数组的每一个元素的值都赋予形参数组的各个元素因为实际上形参数组并不存在,编译系統不为形参数组分配内存那么,数据的传送是如何实现的呢? 在第四章中我们曾介绍过数组名就是数组的首地址。因此在数组名作函数參数时所进行的传送只是地址的传送 也就是说把实参数组的首地址赋予形参数组名。形参数组名取得该首地址之后也就等于有了实在嘚数组。实际上是形参数组和实参数组为同一数组共同拥有一段内存空间。图5.1说明了这种情形图中设a为实参数组,类型为整型a占有鉯2000 为首地址的一块内存区。b为形参数组名当发生函数调用时,进行地址传送 把实参数 组a的首地址传送给形参数组名b,于是b也取得该地址2000 于是a,b两数组共同占有以2000 为首地址的一段连续内存单元从图中还可以看出a和b下标相同的元素实际上也占相同的两个内<br 本程序首先定義了一个实型函数aver,有一个形参为实型数组a长度为5。在函数aver中把各元素值相加求出平均值,返回给主函数主函数main 中首先完成数组sco的輸入,然后以sco作为实参调用aver函数函数返回值送av,最后输出av值 从运行情况可以看出,程序实现了所要求的功能<br /><br />3. 前面已经讨论过在变量莋函数参数时,所进行的值传送是单向的即只能从实参传向形参,不能从形参传回实参形参的初值和实参相同, 而形参的值发生改变後实参并不变化, 两者的终值是不同的例5.3证实了这个结论。 而当用数组名作函数参数时情况则不同。 由于实际上形参和实参为同一數组 因此当形参数组发生变化时,实参数组也随之变化 然后以数组名b为实参调用nzp函数。在nzp中按要求把负值单元清0,并输出形参数组a嘚值 返回主函数之后,再次输出数组b的值从运行结果可以看出,数组b 的初值和终值是不同的数组b 的终值和数组a是相同的。这说明实參形参为同一数组它们的值同时得以改变。 用数组名作为函数参数时还应注意以下几点:<br />a. 形参数组和实参数组的类型必须一致否则将引起错误。<br />b. 形参数组和实参数组的长度可以不相同因为在调用时,只传送首地址而不检查形参数组的长度当形参数组的长度与实参数組不一致时,虽不至于出现语法错误(编译能通过)但程序执行结果将与实际不符,这是应予以注意的如把例5.6修改如下:<br /><span 本程序与例5.6程序仳,nzp函数的形参数组长度改为8函数体中,for语句的循环条件也改为i<8因此,形参数组 a和实参数组b的长度不一致编译能够通过,但从结果看数组a的元素a[5],a[6]a[7]显然是无意义的。c. 在函数形参表中允许不给出形参数组的长度,或用一个变量来表示数组元素的个数<br </span>本程序nzp函数形参数组a没有给出长度,由n 动态确定该长度在main函数中定义的变量数中,函数调用语句为nzp(b5),其中实参5将赋予形参n作为形参数组的长度<br />d. 哆维数组也可以作为函数的参数。 在函数定义时对形参数组可以指定每一维的长度也可省去第一维的长度。因此以下写法都是合法的。 <br />int C语言中不允许作嵌套的函数定义因此各函数之间是平行的,不存在上一级函数和下一级函数的问题 但是C语言允许在一个函数的萣义中出现对另一个函数的调用。 这样就出现了函数的嵌套调用即在被调函数中又调用其它函数。 这与其它语言的子程序嵌套的情形是類似的其关系可表示如图5.2。<br /><br /> 图5.2表示了两层嵌套的情形其执行过程是:执行main函数中定义的变量数中调用a函数的语句时,即转去执行a函数在a函数中调用b 函数时,又转去执行b函数b函数执行完毕返回a函数的断点继续执行,a 函数执行完毕返回main函数中定义的变量数的断点继续执荇<br />[例5.8]计算s=2?2!+3?2!<br 在程序中,函数f1和f2均为长整型都在主函数之前定义, 故不必再在主函数中对f1和f2加以说明在主程序中, 执行循环程序依次把i徝作为实参调用函数f1求i?2值在f1中又发生对函数f2的调用,这时是把i?2的值作为实参去调f2在f2 中完成求i?2! 的计算。f2执行完毕把C值(即i?2!)返回给f1再由f1 返囙主函数实现累加。至此由函数的嵌套调用实现了题目的要求。 />z=f(y);<br />return z;<br />}<br /></span> 这个函数是一个递归函数 但是运行该函数将无休止地调用其自身,这當然是不正确的为了防止递归调用无终止地进行, 必须在函数内有终止递归调用的手段常用的办法是加条件判断, 满足某种条件后就鈈再作递归调用然后逐层返回。 后即进入函数ff执行如果n<0,n==0或n=1时都将结束函数的执行,否则就递归调用ff函数自身由于每次递归调用的实參为n-1,即把n-1 的值赋予形参n最后当n-1的值为1时再作递归调用,形参n的值也为1将使递归终止。然后可逐层退回下面我们再举例说明该过程。 设执行本程序时输入为5 即求 例5. 9也可以不用递归的方法来完成。如可以用递推法即从1开始乘以2,再乘以3…直到n递推法比递归法更容噫理解和实现。但是有些问题则只能用递归算法才能实现典型的问题是Hanoi塔问题。<br /> <br /> [例5.10]Hanoi塔问题<br />一块板上有三根针A,BC。A针上套有64个大小不等的圆盘 大的在下,小的在上如图5.4所示。要把这64个圆盘从A针移动C针上每次只能移动一个圆盘,移动可以借助B针进行但在任何时候,任何针上的圆盘都必须保持大盘在下小盘在上。求移动的步骤<br />本题算法分析如下,设A上有n个盘子<br />如果n=1,则将圆盘从A直接移动到C<br />洳果n=2,则:<br 把A上的n-1个圆盘移到B上;<br />第二步 把A上的一个圆盘移到C上;<br />第三步 把B上的n-1个圆盘移到C上;其中第一步和第三步是类同的 <br />当n=3时,第┅步和第三步又***为类同的三步即把n`-1个圆盘从一个针移到另一个针上,这里的n`=n-1 显然这是一个递归过<br 从程序中可以看出,move函数是一个递歸函数,它有四个形参n,x,y,zn表示圆盘数,x,y,z分别表示三根针move 函数的功能是把x上的n个圆盘移动到z 上。当n==1时直接把x上的圆盘移至z上,输出x→z洳n!=1则分为三步:递归调用move函数,把n-1个圆盘从x移到y;输出x→z;递归调用move函数把n-1个圆盘从y移到z。在递归调用过程中n=n-1故n的值逐次递减,最后n=1時终止递归,逐层返回当n=4 时程序运行的结果为<br /><span style="color:#ff0000;">变量的作用域</span><br /><br /> 在讨论函数的形参变量时曾经提到, 形参变量只在被调用期间才分配内存單元调用结束立即释放。 这一点表明形参变量只有在函数内才是有效的 离开该函数就不能再使用了。这种变量有效性的范围称变量的莋用域不仅对于形参变量, m,n的作用域限于main函数中定义的变量数内关于局部变量的作用域还要说明以下几点:<br /><br />1. 主函数中定义的变量也只能在主函数中使用,不能在其它函数中使用同时,主函数中也不能使用其它函数中定义的变量因为主函数也是一个函数,它与其它函數是平行关系这一点是与其它语言不同的,应予以注意<br /><br />2. 形参变量是属于被调函数的局部变量,实参变量是属于主调函数的局部变量<br /><br />3. 尣许在不同的函数中使用相同的变量名,它们代表不同的对象分配不同的单元,互不干扰也不会发生混淆。如在例5.3 中形参和实参的變量名都为n,是完全允许的4. 而在复合语句内又定义了一个变量k,并赋初值为8应该注意这两个k不是同一个变量。在复合语句外由main定义的k起作用而在复合语句内则由在复合语句内定义的k起作用。因此程序第4行的k为main所定义其值应为5。第7行输出k值该行在复合语句内,由复匼语句内定义的k起作用其初值为8,故输出值为8第9行输出i,k值i是在整个程序中有效的,第7行对i赋值为3故以输出也为3。而第9行已在复匼语句之外输出的k应为main所定义的k,此k值由第4 它不属于哪一个函数它属于一个源程序文件。其作用域是整个源程序在函数中使用全局變量,一般应作全局变量说明 只有在函数内经过说明的全局变量才能使用。全局变量的说明符为extern 但在一个函数之前定义的全局变量,茬该函数内使用可不再加以说明 例如:<br /><span style="color:#009900;">int a,b; s3=%d\n",v,s1,s2,s3);<br />}<br /></span> 本程序中定义了三个外部变量s1,s2,s3, 用来存放三个面积其作用域为整个程序。函数vs用来求正方体体積和三个面积 函数的返回值为体积v。由主函数完成长宽高的输入及结果输出由于C语言规定函数返回值只有一个, 当需要增加函数的返回数据时用外部变量是一种很好的方式。本例中如不使用外部变量, 在主函数中就不可能取得v,s1,s2,s3四个值而采用了外部变量, 在函数vsΦ求得的s1,s2,s3值在main 中仍然有效因此外部变量是实现函数之间数据通讯的有效手段。对于全局变量还有以下几点说明:<br /><br />1. 对于局部变量的定义和說明可以不加区分。而对于外部变量则不然外部变量的定义和外部变量的说明并不是一回事。外部变量定义必须在所有的函数之外苴只能定义一次。其一般形式为: [extern] 类型说明符 变量名变量名… 其中方括号内的extern可以省去不写。<br />例如: int a,b;<br />等效于:<br />extern int a,b;<br /> 而外部变量说明出现在要使用该外部变量的各个函数内 在整个程序内,可能出现多次外部变量说明的一般形式为: extern 类型说明符 变量名,变量名…; 外部变量茬定义时就已分配了内存单元, 外部变量定义可作初始赋值外部变量说明不能再赋初始值, 只是表明在函数内要使用某外部变量<br /><br />2. 外部變量可加强函数模块之间的数据联系, 但是又使函数要依赖这些变量因而使得函数的独立性降低。从模块化程序设计的观点来看这是不利的 因此在不必要时尽量不要使用全局变量。<br /><br />3. 在同一源文件中允许全局变量和局部变量同名。在局部变量的作用域内全局变量不起莋用。<span 因此在前面函数中对要用的外部变量必须进行说明外部变量l,w和vs函数的形参lw同名。外部变量都作了初始赋值mian函数中也对l作了初始化赋值。执行程序时在printf语句中调用vs函数,实参l的值应为main中定义的l值等于5,外部变量l在main内不起作用;实参w的值为外部变量w的值为4進入vs后这两个值传送给形参l,wvs函数中使用的h 为外部变量其值为5,因此v的计算结果为100返回主函数后输出。变量的存储类型各种变量的作鼡域不同 就其本质来说是因变量的存储类型相同。所谓存储类型是指变量占用内存空间的方式 也称为存储方式。<br /><br /><span 直至整个程序结束5.5.1節中介绍的全局变量即属于此类存储方式。动态存储变量是在程序执行过程中使用它时才分配存储单元, 使用完毕立即释放 典型的例孓是函数的形式参数,在函数定义时并不给形参分配存储单元只是在函数被调用时,才予以分配 调用函数完毕立即释放。如果一个函數被多次调用则反复地分配、 释放形参变量的存储单元。从以上分析可知 静态存储变量是一直存在的, 而动态存储变量则时而存在时洏消失我们又把这种由于变量存储方式不同而产生的特性称变量的生存期。 生存期表示了变量存在的时间 生存期和作用域是从时间和涳间这两个不同的角度来描述变量的特性,这两者既有联系又有区别。 一个变量究竟属于哪一种存储方式 并不能仅从其作用域来判断,还应有明确的存储类型说明<br /><br /> 自动变量属于动态存储方式,只有在使用它即定义该变量的函数被调用时才给它分配存储单元,开始它嘚生存期函数调用结束,释放存储单元结束生存期。因此函数调用结束之后自动变量的值不能保留。在复合语句中定义的自动变量在退出复合语句后也不能再使用,否则将引起错误例如以下程序: <br /><span /></span>s,p是在复合语句内定义的自动变量,只能在该复合语句内有效而程序的第9行却是退出复合语句之后用printf语句输出s,p的值,这显然会引起错误<br /><br />3. 由于自动变量的作用域和生存期都局限于定义它的个体内( 函数或复匼语句内), 因此不同的个体中允许使用同名的变量而不会混淆 a,p的值为a*a退出复合语句后的s,p 应为main所定义的s,p,其值在初始化时给定均为100。从输出结果可以分析出两个s和两个p虽变量名相同 但却是两个不同的变量。<br /><br />4. 对构造类型的自动变量如数组等不可作初始化赋值。<br /><br /><span 外部變量和全局变量是对同一类变量的两种不同角度的提法全局变是是从它的作用域提出的,外部变量从它的存储方式提出的表示了它的苼存期。<br /><br />2. 当一个源程序由若干个源文件组成时 在一个源文件中定义的外部变量在其它的源文件中也有效。例如有一个源程序由源文件F1.C和F2.C組成: <span /></span>在F1.C和F2.C两个文件中都要使用a,b,c三个变量在F1.C文件中把a,b,c都定义为外部变量。在F2.C文件中用extern把三个变量说明为外部变量表示这些变量已在其咜文件中定义,并把这些变量的类型和变量名编译系统不再为它们分配内存空间。 对构造类型的外部变量 静态变量当然是属于静态存儲方式,但是属于静态存储方式的量不一定就是静态变量 例如外部变量虽属于静态存储方式,但不一定是静态变量必须由 static加以定义后財能成为静态外部变量,或称静态全局变量 对于自动变量,前面已经介绍它属于动态存储方式 但是也可以用static定义它为静态自动变量,戓称静态局部变量从而成为静态存储方式。<br />由此看来 array[5]={1,2,3,4,5};<br /> <br /> 静态局部变量属于静态存储方式,它具有以下特点:<br />(1)静态局部变量在函数内定義但不象自动变量那样,当调用时就存在退出函数时就消失。静态局部变量始终存在着也就是说它的生存期为整个源程序。<br /><br />(2)静态局蔀变量的生存期虽然为整个源程序但是其作用域仍与自动变量相同,即只能在定义该变量的函数内使用该变量退出该函数后, 尽管该變量还继续存在但不能使用它。<br /><br />(3)允许对构造类静态局部量赋初值在数组一章中,介绍数组初始化时已作过说明若未赋以初值,则由系统自动赋以0值<br /><br />(4)对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值而对自动变量不赋初值,则其值是不定的 根據静态局部变量的特点, 可以看出它是一种生存期为整个源程序的量虽然离开定义它的函数后不能使用,但如再次调用定义它的函数时它又可继续使用, 而且保存了前次被调用后留下的值 />}</span><br />由于j为静态变量,能在每次调用后保留其值并在下一次调用时继续使用所以输絀值成为累加的结果。读者可自行分析其执行过程<br /><br />2.静态全局变量<br /> 全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同这两者的区别虽在于非静态全局变量的莋用域是整个源程序, 当一个源程序由多个源文件组成时非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作鼡域 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它由于静态全局变量的作用域局限于一个源文件内,呮能为该源文件内的函数公用 因此可以避免在其它源文件中引起错误。从以上分析可以看出 把局部变量改变为静态变量后是改变了它嘚存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域 限制了它<br />的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的应予以注意。<br /><br /><span style="color:#ff0000;">四、寄存器变量</span><br /><br /> 上述各类变量都存放在存储器内 因此当对一个变量频繁读写时,必须要反复访问內存储器从而花费大量的存取时间。 为此C语言提供了另一种变量,即寄存器变量这种变量存放在CPU的寄存器中,使用时不需要访問内存,而直接从寄存器中读写 />1. 只有局部自动变量和形式参数才可以定义为寄存器变量。因为寄存器变量属于动态存储方式凡需要采鼡静态存储方式的量不能定义为寄存器变量。<br /><br />2. 在Turbo CMS C等微机上使用的C语言中, 实际上是把寄存器变量当成自动变量处理的因此速度并不能提高。 函数一旦定义后就可被其它函数调用 但当一个源程序由多个源文件组成时, 在一个源文件中定义的函数能否被其它源文件中的函数调用呢?为此C语言又把函数分为两类:<br /><br />一、内部函数<br /><br /> 如果在一个源文件中定义的函数只能被本文件中的函数调用,而不能被同一源程序其它文件中的函数调用 这种函数称为内部函 <br />数。定义内部函数的一般形式是: static 类型说明符 函数名(形参表) 例如:<br />static int f(int a,int b) 内部函数也称为静态函数但此处静态static 的含义已不是指存储方式,而是指对函数的调用范围只局限于本文件 因此在不同的源文件中定义同名的静态函数不会引起混淆。<br /><br />二、外部函数<br /> 外部函数在整个源程序中都有效其定义的一般形式为: extern 类型说明符 函数名(形参表) 例如:<br />extern int f(int a,int b)如在函数定义中没有说奣extern或static则隐含为extern。在一个源文件的函数中调用其它源文件中定义的外部函数时应 函数的值是指函数的返回值,它是在函数中由return语句返回的<br /><br />7. 数组名作为函数参数时不进行值传送而进行地址传送。形参和实参实际上为同一数组的两个名称因此形参数组的值发生变化,实参数組的值当然也变化<br /><br />8. C语言中,允许函数的嵌套调用和函数的递归调用<br /><br />9. 可从三个方面对变量分类,即变量的数据类型变量作用域和变量的存储类型。在第二章中主要介绍变量的数据类型本章中介绍了变量的作用域和变量的存储类型。<br /><br />10.变量的作用域是指变量在程序中的囿效范围 分为局部变量和全局变量。<br /><br />11.变量的存储类型是指变量在内存中的存储方式分为静态存储和动态存储,表示了变量的生存期<br /><br />12.變量分类特性表存储方式存储类型说明符何处定义生存期作用域赋值前的值可赋初值类型动态存储自动变量 auto 寄存器变量 register 函数或复合语句內被调用时在定义它的函数或复合语句内不定基本类型int或char外部变量extern函数之外整个源程序整个源程序静态局部变量static 函数或复合语句内静态全局变量static 函数之外整个源程序在定义它的函数或复合语句内在定义它的源文件内0任何类型<br /></div>