《函数和编译预处理精选PPT.ppt》由会员分享,可在线阅读,更多相关《函数和编译预处理精选PPT.ppt(97页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、关于函数和编译预处理第1页,讲稿共97张,创作于星期日3.1 概述概述函数:functionfunction,功能。功能。执行或承担某一功能的执行或承担某一功能的程序段程序段,解答某问题的,解答某问题的一段程序一段程序。它有特定的它有特定的组织格式组织格式和和使用方式使用方式。函数 与 程序的关系:一个复杂的C语言程序包含多个函数。(主函数main)它们可存放在多个文件中。模块化程序设计思想:划分程序功能,每个子功能用子函数(主函数之外的函数)来承担,而让 主函数 去 调用(使用)这些子函数 从而实现程序的所有功能。每个函数单独设计调试,而且可以重复使用。第2页,讲稿共97张,创作于星期日ma
2、in()func1()func2()func3()func5()func4()主调函数主调函数、调用、调用被调函数(子函数)、被调用参数返回值主调函数主调函数 和和 被调函数 的关系:上下级 关系请问:main()被 谁调用?一个程序中 函数 调用 的示意图:第3页,讲稿共97张,创作于星期日#include void main()int n;cinn;if(n1)cout“the sum is:”sum(n)endl;/调用调用 sum()函数函数 int sum(int n)例3.1 编写函数求前 n 个自然数之和,n 的值从键盘输入。函数首部、函数头函数首部/函数头函数体 int k,s
3、=0;for(k=1;ky)?x:y;return z;void main(void)int a,b,c;cinab;c=max(a,b);cout“The max is”cy)?x:y;return z;void main(void)int a,b,c;cinab;c=max(a,b);cout“The max is”cendl;ab23调用处(表达式中)调用处(表达式中)3xy23zc=3不要将函数名做变量用!不能写成 max=z;第14页,讲稿共97张,创作于星期日(2)自定义函数的声明函数类型关键字 函数名(参数1 类型,参数1名称 ,参数2 类型,参数2名称 );什么时候需要声明?声
4、明的含义、作用?声明的格式、位置?(1)库函数的声明 对库函数的声明已经写在有关 包含文件(头文件)中了,因此只要在程序文件首部用 include 指令将这些 包含文件 包含到本程序中来,就完成了对库函数的声明。#include 3.2.2 函数的声明(补述)函数的声明形式也叫做函数原型(function prototype)。它说明了函数的类型、函数名、函数各形式参数类型。函数类型关键字 函数名(参数1 类型 ,参数2 类型 );修改p68名称任意,可省略第15页,讲稿共97张,创作于星期日【例例3.2】输入输入圆柱体圆柱体的底面半径和高,求底面的底面半径和高,求底面圆圆面积面积和和体积体积
5、。设函数设函数 area()和和 volum()分别求分别求圆圆面积和圆柱体体积。面积和圆柱体体积。#include using namespace std;void main()double volum(float,float);/声明声明 double area(float r);/声明声明 float r,h;double s,v;coutrh;s=area(r);/调用调用 v=volum(r,h);/调用调用 couts=s,v=vab;c=add(a,3.5);cout“The sum is”cendl;(2)实参 与 形参的类型应相同或赋值兼容。第18页,讲稿共97张,创作于星期
6、日 值传递:调用函数时,计算机将函数调用处的实参值传给被调函数的形参,在被调函数执行过程中,形参可以被改变,但不影响函数调用处的实参值。换一句话说,这种参数传递机制是单向影响。3.3.1 参数的 值传递(前面已介绍)第19页,讲稿共97张,创作于星期日 除了3.3.2小节介绍的值传递参数方式外,函数调用还有一种特殊的值传递形式,即传递的值不是一般的数值,而是一些内存单元地址编号(即地址),这时,一般称之为参数的地址传递。在这种参数传递形式中,无论在函数的定义中出现的形参还是在调用语句中出现的实参,都是代表一些内存单元地址编号(即地址数值),而不是一般的数值。C+中的参数地址传递情况一般有如下几
7、种:实参可以是一个有确定值的普通变量的地址,或者是一个已经初始化的指针变量;或者是一个初始化的数组名;或者是一个具体的函数名。而形参可以是一个任意普通变量的地址,或是一个任意指针变量,或是一个任意的数组名,或是一个指向函数的指针变量(对应于实参是具体函数名)。3.3.2 参数的 地址传递(4章)第20页,讲稿共97张,创作于星期日 实际上,这种参数传递机制就是在函数调用时把一个内存单元地址传递给形参,使形参也具有实参的内存单元地址(即两者对应同一个内存单元),称作形参和实参地址结合,两者合二为一。这样一来,任何时候形参的值等于实参的值;而实参的值也等于形参的值。因此,形参在函数中发生变化后,也
8、会引起实参跟着变化(因为它们是捆绑在一起的,一体化的)。这就意味着按地址传递的方式,在调用刚开始时实参的值影响了形参;而在被调函数执行过程中形参值若发生了变化,它也会影响实参的值变化。即机制是双向影响,这与普通值传递方式的单向影响机制形成对比。第21页,讲稿共97张,创作于星期日【例例3.5】延迟函数的使用。延迟函数的使用。#include void delay(int loop);/声明声明void main()coutbeginendl;delay(1000);/调用调用 coutendendl;void delay(int loop)/定义定义 if(loop=0)return;for(
9、int i=0;iloop;i+)coutiendl;3.3.3 带 默认值 的参数(C+)C+语言中,允许在语言中,允许在函数定义函数定义或或声明声明时给一个或多个形参指定时给一个或多个形参指定默默认值认值。这样,后面的。这样,后面的函数调用函数调用中,可以不给具有中,可以不给具有默认值默认值的形参设定相的形参设定相应的实参。应的实参。void delay(int loop=1000);void delay(int x=1000);delay();/调用调用 第22页,讲稿共97张,创作于星期日 对于教材【例3.2】的求圆柱体体积的函数volume(),如下声明:float volume(f
10、loat r,float h=8.5);/只对形参h指定默认值8.5这时函数调用形式:volume(6.0);/相当于volume(6.0,8.5)volume(6.0,7.2);/r的值为6.0,h的值为7.2 如果函数有多个形参,可以对每个或部分形参指定默认值。指定默认值的形参必须放在参数列表中的 最 右 边。第23页,讲稿共97张,创作于星期日课堂总结第24页,讲稿共97张,创作于星期日a 函数函数b 函数函数main 函数函数结束结束(2)(3)(4)(5)(6)(7)(8)(9)C C语语言言不允许不允许对函数作对函数作嵌套定义!嵌套定义!3.4.1 3.4.1 嵌套调用:嵌套调用:
11、在一个在一个函数定义函数定义中,可以中,可以调用调用另一个函数另一个函数。程序执行时,在调用一个函数的过程中,又调用程序执行时,在调用一个函数的过程中,又调用另一个函数另一个函数。例如例如:3.4 函数的 嵌套调用 和 递归调用调用调用 a 函数函数调用调用 b 函数函数main()函数函数 直接地 调用了 a a函数函数,main()函数函数 嵌套地 调用了 b b函数函数(间接调用间接调用)。第25页,讲稿共97张,创作于星期日long comb(int n,int m)/定义组合函数long c;c=fac(m)/(fac(n)*fac(m-n);/嵌套调用阶乘函数 return c;v
12、oid main()int n,m;long c;coutplease input two integer numbers:m,nmn;c=comb(n,m);/调用组合函数combcoutc=cendl;main()分析:定义函数long comb(int n,int m)求组合数。定义函数long fac(int k)求k的阶乘。comb()fac()#include using namespace std;long fac(int k)/定义求阶乘的函数long f=1;int i;for(i=1;i=k;i+)f=f*i;return f;【例3.6】编程求组合数,第26页,讲稿共97
13、张,创作于星期日3.4.2 函数的 递归调用 在调用一个函数的过程中,被调用函数又直接或间接地调用 自身,这种调用过程称为函数的递归(recursive)调用。直接递归 调用 函数 的代码形式:int f()/函数f1的定义 /函数其它部分 z=f();/直接调用自身 /函数其它部分 在函数f()中,又直接调用了f()函数。第27页,讲稿共97张,创作于星期日间接递归 调用 函数 可以表现为如下:int f1()/函数f1的定义 /f1的其他部分x=f2();/调用f2()/f1的其他部分 int f2()/函数f2的定义 /f2的其他部分y=f1();/调用f1()/f2的其他部分 第28页
14、,讲稿共97张,创作于星期日函数的直接递归调用 函数的间接递归调用 图中调用过程特点:这两种递归调用都是无终止的自身调用?!程序中不应出现这种无终止的递归调用,而只应出现有限次数的、有终止的递归调用!解决:用if语句来控制,只有在某一条件成立时才继续执行递归调用,否则就不再继续。包含递归调用的函数称为递归函数。第29页,讲稿共97张,创作于星期日v【例3.7】用递归计算 n!。求n!,应先求(n-1)!;而求(n-1)!,又需要先求(n-2)!,而求(n 2)!;又可以变成求(n-3)!,如此继续,直到最后变成求1!的问题,而根据公式有1!=1(这就是本问题的递归终止条件)。由终止条件得到1!
15、结果后,再反过来 依次求出2!,3!直到最后求出n!。第30页,讲稿共97张,创作于星期日v【例3.7】用递归计算n!。n!本身就是以递归的形式定义的:#include using namespace std;long fac(int n)long f;if(n=1)f=1;else f=n*fac(n-1);/递归调用,求(n-1)!return f;void main()long y;int n;coutplease input a integer n n;y=fac(n);/调用fac(n)求n!coutn=n,y=y1)long fibonacci(int n)if(n=0|n=1)r
16、eturn n;elsereturn fibonacci(n-1)+fibonacci(n-2);程序如下所示:程序如下所示:实验教程实验教程P19 三三 实验思考实验思考 1:求求Fibonacci数列数列 大于大于t的最小项的最小项 的值。的值。第34页,讲稿共97张,创作于星期日void main()int n=0,t;long result,t;cint;result=fibonacci(n);whlie(result=t)n+;result=fibonacci(n);coutresult;方法:1.递归函数实验教程:实验教程:P19 三三 实验思考实验思考 1 2.用循环做 实验教程
17、:实验教程:p90.四四,1 3.数组 教材:教材:p140,3第35页,讲稿共97张,创作于星期日求两个数最大公约数的方法最大公约数的方法1.辗转相除法辗转相除法 (p57)2.短除法短除法3.试探法试探法4.递归方法递归方法 (用(用 函数递归调用函数递归调用 做)做)用 g(m,n)表示m、n的最大公约数最大公约数,请写出则其递归定义。请写出则其递归定义。第36页,讲稿共97张,创作于星期日递归边界(终止)条件递归边界(终止)条件使问题向递归边界(终止)条件转化的规则使问题向递归边界(终止)条件转化的规则类似例题类似例题习题:习题:强调:上述问题的递归定义强调:上述问题的递归定义具备两个
18、成分:具备两个成分:第37页,讲稿共97张,创作于星期日递推方法递推方法程序描述繁杂,可读性差;程序描述繁杂,可读性差;主要采用主要采用 循环结构;循环结构;逐步执行;逐步执行;当前值的求得总建立在前面求解的基础上;当前值的求得总建立在前面求解的基础上;递归方法递归方法描述与原始问题(递归公式)比较接近;描述与原始问题(递归公式)比较接近;书写简洁、易读易写;书写简洁、易读易写;易于分析算法的复杂性和证明算法的正确性;易于分析算法的复杂性和证明算法的正确性;在问题转化时,需要花时间和存储空间将有关的在问题转化时,需要花时间和存储空间将有关的“现场现场信息信息”保存起来;当达到终止条件时,系统又
19、需要花时保存起来;当达到终止条件时,系统又需要花时间将有关的间将有关的“现场信息现场信息”恢复以便处理未曾处理完的函恢复以便处理未曾处理完的函数调用数调用。占用存储空间少,执行速度快。占用存储空间少,执行速度快。递归递归 与与 递推递推 算法算法递归函数的适应场合:待求解的问题含有递归关系。第38页,讲稿共97张,创作于星期日【例3.8】汉诺塔问题abc汉诺塔(Tower of Hanoi)问题据说来源于布拉玛神庙。该问题的装置如图3.6所示(图上仅画三个金片以简化问题的原理,原问题有64个金片),底座上有三根金钢石的针,第一根针a上放着从大到小64个金片。解决该问题就是要想法把所有金片从第一
20、根针a上移到第三根针c上,第二根针b作为中间过渡。要求是每次只能移动一个金片,并且任何时候不允许大的金片压在小的金片上面。图3.6 三个金片的汉诺塔问题装置第39页,讲稿共97张,创作于星期日1.本问题的递归终止条件。如果只有1个盘,显然问题的解就很明显是:直接把金片从a移到c。因此终止条件是n=1;终止条件对应的操作是直接把金片从a移到c,示意ac。2.本问题的递归分析:移动n个金片从a到c,必须先将n-1个金片从a借助c移动到b,移动n-1个金片与原问题相同,但规模变小,即向终止条件接近,因此,此问题可以用递归过程完成。递归过程可以用如下步骤表示:(1)将n-1个金片从a经过c移动到b。(
21、2)将第n个金片从a直接移动到c。(3)再将n-1个金片从b经过a移动到c。第40页,讲稿共97张,创作于星期日一般地,设将n个金片从x针借助y针移动到z针的函数原形为:void hanoi(int n,char x,char y,char z)根据解题步骤,可以写出求解n个金片的汉诺塔函数如下:#include /*ex3_8.cpp*using namespace std;void hanoi(int n,char x,char y,char z)if(n=1)/n=1时,直接将金片从x移动到z cout x z 1时 hanoi(n-1,x,z,y);/先将n-1个金片从借助z移动到y
22、cout x z 1时,就递归调用hanoi(),每次n减1。最后当n=1时,直接移动该金片就可以了。主函数如下:void main()int n;cout input n:n;hanoi(n,a,b,c);/n个金片从a针借助b针移动到c针 虽然递归调用在写程序时很简单,但执行起来却很复杂(时间、存储空间都开销大)。对于汉诺塔问题程序的执行过程分析比较复杂,有兴趣的读者可参阅教材对3个盘情景的分析(图3.7 及其相应文字叙述)。第42页,讲稿共97张,创作于星期日3.5 内置函数(C+,选讲)一般函数调用的过程:调用前:参数传递,保存下条指令地址信息,和CPU状态信息,执行流程转入被调函数。
23、调用完毕:传回函数值,执行流程回到主调函数。函数调用花费额外的时间和空间!如果函数频繁地被调用,如调用出现在循环中,则开销大,影响效率(缺点)。函数的优点之一:便于实现模块化/结构化程序设计的方法。发挥其优点,克服其缺点,采用内置函数第43页,讲稿共97张,创作于星期日3.5 内置函数(C+,选讲)一般函数调用的过程:C+编译时将被调函数的代码直接嵌入到主调函数中,程序执行时不必将流程转出去。这种嵌入到主调函数中的函数称为内置函数(inline function),又称内嵌函数 或 内联函数。第44页,讲稿共97张,创作于星期日3.5.1 内置函数的作用 提高程序中函数调用的效率;并保持程序的
24、可读性。适应场合:如果函数频繁地被调用.指定内置函数的方法:在函数定义首行(或函数说明)的左端加一个关键字inline即可。第45页,讲稿共97张,创作于星期日#include int is_number(char);/函数声明void main()char c;while(c=cin.get()!=n)if(is_number(c)/调用一个小函数coutyou enter a digit n;else cout=0&ch=9)?1:0;【例3.9】判断用户从键盘输入的系列字符是数字字符还是其它字符的函数is_number()。第46页,讲稿共97张,创作于星期日程序中不断到设备中读取数据,
25、频繁调用程序中不断到设备中读取数据,频繁调用is_number()函数。为了函数。为了避免频繁调用函数,提高执行效率,可以将避免频繁调用函数,提高执行效率,可以将【例例3.9】程序改为:程序改为:#include /*ex3_9b.cpp*void main()char c;while(c=cin.get()!=n)if(c=0&c=9)?1:0)/修改处:直接计算表达式修改处:直接计算表达式 coutyou enter a digit n;else coutyou enter a non-digit n;第47页,讲稿共97张,创作于星期日修改后的程序在if语句中用表达式替换了函数调用。在程
26、序运行上,提高了一些执行效率,因为免去了大量的函数调用开销。但是,由于is_number函数比相应的表达式可读性好,所以修改后的代码可读性降低,尤其若程序中多处出现is_number的替换时,会大大降低可读性。我们希望既要用函数调用来体现其结构化和可读性,又要使效率尽可能地高。为了尽量做到两全其美,C+中引入内置函数这个方法。第48页,讲稿共97张,创作于星期日内置函数的定义格式如下:inline 类型名 函数名(形参列表)/函数体 内置函数的声明格式如下:inline 类型名 函数名(形参类型表);其实,内置函数只要在开头一次性声明为inline即可,而后面的函数定义仍可写成一般函数定义的形
27、式,编译器也会将函数视为内置函数。3.5.2 定义 和 使用 内置函数第49页,讲稿共97张,创作于星期日对【例3.9】使用内置函数来解决,代码可以写成下列形式:#include /*ex3_9c.cpp*inline int is_number(char);/inline函数声明函数声明void main()char c;while(c=cin.get()!=n)if(is_number(c)/调用一个小函数调用一个小函数coutyou enter a digit n;else cout=0&ch=9)?1:0;第50页,讲稿共97张,创作于星期日(1)内置函数与一般函数的区别在于函数调用的
28、处理。一般函数进行调用时,要将程序执行到被调用函数中,然后返回到主调函数中;而内置函数在调用时,是将调用部分用内置函数体来替换。(2)若函数定义部分在调用之后,则内置函数必须先声明后调用。因为程序编译时要对内置函数替换,所以在内置函数调用之前必须声明是内置的(inline),否则将会像一般函数那样产生调用而不是进行替换操作。(3)在内置函数中,不能含有复杂的结构控制语句,如switch、for和while。如果内置函数有这些语句,则编译器将该函数视同一般函数那样产生函数调用。(4)递归函数不能用作内置函数。(5)以后讲到的类中,所有定义在说明内部的函数都是内置函数。关于内置函数的说明:第51页
29、,讲稿共97张,创作于星期日3.6 变量 和 函数 的属性变量的属性:类型、名称、作用域、生存期 3.6.1 变量的作用域 作用域:指变量在指变量在 空间上空间上 的有效范围。的有效范围。1局部变量局部变量:在函数或程序块(复合语句)内定义的变量。其作用域:相应的函数体 或 程序块,从定义点到相应的函数体 或 程序块的尾部。void f(int t)/t为函数级的局部变量。int x1;/x1为函数级的局部变量。int y1;/y1为语句块级的局部变量,int y2;/y2为语句块级的局部变量,int x2;/x2为函数级的局部变量。第52页,讲稿共97张,创作于星期日v【例3.10】局部变量
30、的使用。v#include vdouble fun1(double a,double b)v double c;/fun1函数中3个局部变量a、b、cvc=a+b;vreturn c;v void main()/main函数中3个局部变量a、b、cdouble a,b,c;coutab;c=fun1(a,b);couta+b=cendl;c=fun2(a,b);couta*b=cendl;局部变量同名现象:不同范围的局部变量可以同名,不同范围的同名局部变量表示不同的数据。但同一范围内不允许两变量同名出现。double fun2(double a,double b)double c;/fun2函
31、数中3个局部变量a、b、cc=a*b;return c;第53页,讲稿共97张,创作于星期日注意:注意:在在函数声明函数声明中出现的参数名,其作用范中出现的参数名,其作用范围只在本行的括号内。例如:围只在本行的括号内。例如:int max(int a,int b);/函数声明中出现的函数声明中出现的a、b,它们的作用范围只在本行有效。,它们的作用范围只在本行有效。int max(int,int);第54页,讲稿共97张,创作于星期日v 定义在函数以外的变量(外部变量,静态全局变量)。v作用域作用域:从变量定义处到该文件结尾处。通过声明后,对本程序的其它文件中的函数也可见。2全局变量第55页,讲
32、稿共97张,创作于星期日#include int a;/a的作用域为整个文件void fun1();/声明fun1函数void main()coutaendl;/main函数中使用了全局变量afun1();/调用fun1函数coutaendl;/main函数中再次使用全局变量avoid fun1()a=5;/fun1函数中使用全局变量a 程序运行结果如下:05没有设定初始值的全局变量,编译默认其初值为0。【例3.11】全局变量 的使用第56页,讲稿共97张,创作于星期日说明:(1)没有人为设定初始值的全局变量,编译将其初值设为0(全局变量默认初始值为0)。(2)全局变量可以定义在任何位置,但其
33、作用域是从定义的位置开始到文件的末尾。而定义在文件中间的全局变量就只能被其下面的函数所使用,全局变量定义之前的所有函数不会知道该变量。(3)全局变量 为函数之间 数据的传递 提供了通道。由于全局变量可以被其定义后的函数所使用,所以可以使用全局变量进行函数间数据的传递。而且这种传递数据的方法可以传递多个数据的值。第57页,讲稿共97张,创作于星期日gcd(a,b)maxlcm(a,b)main()min分析:由于求最小公倍数要依赖于最大公约数,所以应先求最大公约数。故应将求分析:由于求最小公倍数要依赖于最大公约数,所以应先求最大公约数。故应将求最大公约数的函数写在前面,求最小公倍数的函数放在后面
34、。最大公约数的函数写在前面,求最小公倍数的函数放在后面。int gcd(int,int);/声明求最大公约数的函数声明求最大公约数的函数int lcm(int,int);/声明求最小公倍数的函数声明求最小公倍数的函数int max,min;/全局变量分别存放全局变量分别存放 最大公约数、最小公倍数最大公约数、最小公倍数void main()【例例3.12】分别写两个函数求给定两个数的最大公约数和最小公倍数。其中,要求分别写两个函数求给定两个数的最大公约数和最小公倍数。其中,要求用全局变量存放最大公约数和最小公倍数。用全局变量存放最大公约数和最小公倍数。第58页,讲稿共97张,创作于星期日分析:
35、由于求最小公倍数要依赖于最大公约数,所以应先求最大公约数。故应将求最大公约数的分析:由于求最小公倍数要依赖于最大公约数,所以应先求最大公约数。故应将求最大公约数的函数写在前面,求最小公倍数的函数放在后面。函数写在前面,求最小公倍数的函数放在后面。#include int gcd(int,int);/声明求最大公约数的函数声明求最大公约数的函数int lcm(int,int);/声明求最小公倍数的函数声明求最小公倍数的函数int max,min;/全局变量分别存放最大公约数、最小公倍数全局变量分别存放最大公约数、最小公倍数void main()int a;int b;coutab;gcd(a,b
36、);lcm(a,b);cout the greatest common divisor is:maxendl;/使用全局变量使用全局变量 max cout the lease common multiple is:minendl;/使用全局变量使用全局变量 min【例例3.12】分别写两个函数求给定两个数的最大公约数和最小公倍数。其中,要求用全局分别写两个函数求给定两个数的最大公约数和最小公倍数。其中,要求用全局变量存放最大公约数和最小公倍数。变量存放最大公约数和最小公倍数。第59页,讲稿共97张,创作于星期日int gcd(int x,int y)int t;int r;if(xy)t=x;
37、x=y;y=t;r=x%y;while(r!=0)x=y;y=r;r=x%y;max=y;return max;/使用全局变量使用全局变量maxint lcm(int x,int y)min=x*y/max;/使用全局变量使用全局变量max求全局变量求全局变量min,return min;/返回全局变量返回全局变量min void gcd(int x,int y)int t;int r;if(xy)t=x;x=y;y=t;r=x%y;while(r!=0)x=y;y=r;r=x%y;max=y;第60页,讲稿共97张,创作于星期日(4)全局变量降低了函数的通用性,建议不在必要时不要使用全局变量
38、(缺点)。因为如果函数在执行的时候使用了全局变量,那么其他程序使用该函数时也必须将该全局变量一起移过去。另外,全局变量在程序执行的全部过程都占用存储空间,而不是需要时才开辟存储空间。第61页,讲稿共97张,创作于星期日3不同作用域的同名变量引用规则不同作用域的同名变量引用规则 程序运行结果是:程序运行结果是:1,2,3,5【例例3.13】不同作用域的同名变量引用规则示例。不同作用域的同名变量引用规则示例。int a=5;/全局变量全局变量aint fun();/声明一个函数声明一个函数main()int a;/函数级局部变量函数级局部变量a a=1;/引用是函数级局部变量引用是函数级局部变量a
39、 couta”,”;/此处引用的是函数级局部变量此处引用的是函数级局部变量a int a;/程序块级局部变量程序块级局部变量a a=2;/此处引用是程序块级局部变量此处引用是程序块级局部变量a couta”,”;/此处引用的是程序块级局部变量此处引用的是程序块级局部变量a a=3;/此处引用的此处引用的a是函数级局部变量是函数级局部变量a couta”,”;/此处引用的此处引用的a是函数级局部变量是函数级局部变量a coutfun()endl;int fun()return a;/全局变量全局变量a 重名变量作用域规则重名变量作用域规则:在:在某个某个作用域范围作用域范围内定义内定义的变量,在
40、该范围的的变量,在该范围的子子范围范围内可以定义重名变量,内可以定义重名变量,这时原定义的变量在子范这时原定义的变量在子范围内是不可见的,但是它围内是不可见的,但是它还存在,只是在子范围内还存在,只是在子范围内由于出现了重名的变量而由于出现了重名的变量而被暂时隐藏起来,过了子被暂时隐藏起来,过了子范围后,它又是可见的。范围后,它又是可见的。第62页,讲稿共97张,创作于星期日3.6.2 变量的生存期(较难)变量存储期(生命期):变量(值)在内存中存在的时间。动态存储区(堆栈)静态(全局)存储区寄存器(CPU中)数据存储区(变量存储区)1短生存期变量动态存储方式2长生存期变量静态存储方式变量存储
41、期长短 由变量的 存储方式 决定。动态存储方式:在程序运行期间动态地分配存储空间给变量的方式。包括:函数的形参,函数或程序块中定义的局部变量(未用static声明)。具体有两种:自动变量、寄存器类变量。静态存储方式:在程序运行期间分配固定的存储空间给变量的方式。存储位置:静态(全局)存储区,生存期:为程序运行期间。包括:全局变量(含外部变量、静态全局变量)、静态局部变量。存储位置:在动态存储空间(堆或栈)或寄存器中。存储期:为函数或程序块的运行期间。第63页,讲稿共97张,创作于星期日存储位置:在动态数据存储区;生存期:函数 或 程序块 执行期间;作用域:其所在函数或程序块。用关键字auto作
42、为存储类别的声明。例如:int fun()auto int a;/a为自动类变量 关键字“auto”可以省略。上述自动变量也可声明为 int a;(1)自动变量:函数中的局部变量默认是自动变量。(2)寄存器变量:局部变量。存储位置:在CPU的通用寄存器中;生存期:作用域:特点:访问效率高,数量较少;用关键字register作为存储类别的声明。例如:void main()register int i;第64页,讲稿共97张,创作于星期日1)寄存器变量不宜定义过多。计算机中寄存器数量是有限的,不能允许所有的变量都为寄存器变量。如果寄存器变量过多或通用寄存器被其他数据使用,那么系统将自动把寄存器变量
43、转换成自动变量。2)寄存器变量的数据长度与通用寄存器的长度相当。一般是char型和int型变量。寄存器变量 的使用应注意以下问题:第65页,讲稿共97张,创作于星期日2长生存期变量静态存储方式全局变量(含外部变量、静态全局变量)和 静态局部变量。(1)外部变量:未用static关键字定义的全局变量。如果不对外部变量另加声明,则它的作用域是从定义点到所在文件的末尾。第66页,讲稿共97张,创作于星期日用extern关键词加以声明后将外部变量作用域扩展到声明位置,声明语句格式为extern 类型符 外部变量名;/int 类型符可省 改具体方式:提前引用声明。扩展 外部变量 作用域 的方式【例3.1
44、4】对定义在同一文件中外部变量,作提前引用声明 可以扩展其使用范围到文件前面的 声明 位置。#include void main()x=4;coutxendl;int x;/外部变量x的定义 extern int x;/提前引用声明第67页,讲稿共97张,创作于星期日 跨文件引用声明。扩展 外部变量 作用域 的方式【例3.15】对定义在另一文件中的外部变量,作跨文件引用声明以扩展其作用域到 本文件。文件 file1.cpp 的内容:#include void main()coutwendl;/使用file2.cpp文件中定义的变量w 文件 file2.cpp 的内容:int w=10;/外部变
45、量 w 的定义程序运行结果如下:10 extern int w;/跨文件引用声明用途:用于多个文件中的函数共享数据!两个文件中不能出现 同名的外部变量!第68页,讲稿共97张,创作于星期日(2)静态变量:静态全局变量、静态局部变量 静态全局变量:在定义全局变量时开头再添加一个static关键字所定义的全局变量。作用域:定义点至定义所在文件的末尾;可以通过提前引用声明扩展其作用域;不能通过跨文件引用声明扩展其作用域(只对本文件有效)。一个文件中定义的静态全局变量与别的文件中定义的同名静态全局变量或同名外部变量没有牵连,互不影响!默认初值:0。第69页,讲稿共97张,创作于星期日v【例3.17】静
46、态全局变量 的演示。文件file1.cpp的内容:#include static int u=10;/定义静态全局变量 void fun()cout”This is file1”文件file2.cpp的内容:#include extern int u;/试图对u 作跨文件引用声明,此时行不通void main()coutuendl;/出现“变量u未定义”错误第70页,讲稿共97张,创作于星期日(2)静态变量:静态全局变量、静态局部变量 静态局部变量:在定义局部变量时开头再添加一个static关键字所定义的局部变量。生命周期:作用域:默认初值:0。第71页,讲稿共97张,创作于星期日v【例3.1
47、6】使用静态局部变量的例子。#include void fun();void main()int i;for(i=0;i3;i+)fun();void fun()int a=0;/定义局部变量定义局部变量 static int b=0;/定义静态局部变量定义静态局部变量 a=a+1;b=b+1;couta,bendl;程序运行结果如下:1,11,21,3 第72页,讲稿共97张,创作于星期日变量属性 总结根据需要设置变量的属性!第73页,讲稿共97张,创作于星期日3.6.3 内部函数 和 外部函数(选讲)存储属性(作用域)分别 类似 静态全局变量、外部变量1 内部函数(存储属性 类似静态全局变
48、量)如果一个函数只能被 本文件 中 其他函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类型的前面加static。函数首部的一般格式为static 类型标识符 函数名(形参表)如static int fun(int a,int b)内部函数又称 静态(static)函数。使用内部函数,可以使函数只局限于所在文件。不同文件中的同名内部函数互不干扰。第74页,讲稿共97张,创作于星期日v【例3.18】静态函数的例子。文件 file1.cpp 中的内容:#include static void fun();void main()fun();static void fun()/文件file
49、1中定义静态函数,名称为fun coutthis in file1 endl;文件 file2.cpp 中的内容:#include static void fun()/文件file2中定义静态函数,名称也为fun coutthis in file2 endl;程序运行结果如下:this in file1 第75页,讲稿共97张,创作于星期日2 外部函数(存储属性 类似外部变量)外部函数是可以被 整个程序 各文件中函数 调用的函数。(1)外部函数的定义。在函数类型前加存储类型关键字extern,或缺省存储类型关键字,定义格式如下:extern 函数类型 函数名(参数列表)函数体 extern可缺
50、省,即系统默认为extern型。(2)外部函数的(跨文件引用)声明。文件A 在需要调用 文件B 中所定义的外部函数时,需要在文件A中用关键字extern对被调函数提出声明,声明格式如下:extern 函数类型 函数名(参数类型列表)第76页,讲稿共97张,创作于星期日【例3.19】文件file1.cpp利用别的文件(file2.cpp)中的外部函数实现求阶乘。文件file1.cpp中的内容:#include using namespace std;void main()extern double fac(int);/声明其他文件中定义的声明其他文件中定义的 外部函数外部函数int n;cout