资源描述
Chapter 3 函数和程序结构3.1 函数的组织3.2 局部变量和全局变量、命名空间std3.3 形参与实参3.4 引用和const3.5 缺省参数值3.6 函数重载3.7 内联函数3.8 编译预处理3.1 函数的组织函数的组织结构化程序设计方法结构化程序设计方法使用结构化程序设计方法解决复杂的问题:分解大问题小问题更小问题求解(调用)main()函数函数一个函数是根据进去的信息(输入)和产生的东西(输出结果)所定义的一个黑盒。函数用于把较大的计算任务分解成若干个较小的任务,使程序人员可以在其他函数的基础上构造程序,而不需要从头做起。一个设计得当的函数可以把具体操作细节对程序中不需要知道它们的那些部分隐藏掉,从而使整个程序结构清楚,减轻了因修改程序所带来的麻烦。程序示例程序示例计算计算(12+10)!/(12!+10!)for example:ch3/3-1-1.cpp for example:ch3/3-1-2.cpp2层结构,2个函数,降低程序的构思、编写、调试的复杂度,可读性好函数定义返回类型 函数名(参数声明列表)函数体;double max(double d1,double d2)return(d1d2?d1:d2);return语句用于从被调用函数向调用者返回值。没有返回值的函数、没有参数的函数,用关键字void修饰。void f(void)cout “aaaa”d2?d1:d2);Void main()double f1,f2;cin f1 f2;cout max(f1,f2);cout max(67.4,23);形参实参实际参数可以是一个常量,变量,或甚至可以是一个表达式。3.2 局部变量和全局变量局部变量和全局变量变量作用范围(作用域)在函数内定义的变量(包括形参)局部变量 作用范围:本函数内部 定义在复合语句内的变量 作用范围:复合语句内部全局变量 在函数以外定义的变量,不从属于任一函数 作用范围:从定义处到源文件结束3-4.cpp变量作用范围示例int x=1;void main()int a=2;.int b=3;.f();.int t=4;void f()int x=5,b=6;.int a=7;x=?a=?b=?b=?x=?b=?t=?a=?x=1 a=2 b=3b没定义 x=5 b=6 t=4 a没定义 若局部变量与全局变量同名,局部变量优先变量作用范围示例int x=1;int f(int x)return(x+);main()int y;y=f(2);x=f(x);printf(“%d%d”,y,x);x=1变量作用范围l如果局部变量与全局变量同名,局部变量优先。l不要滥用全局变量有副作用int x=1;void f1()x+;void f2()x=5;void main()x=10;f1();cout x;f2();cout x;11 5 程序是变量(全局)定义和函数定义的集合。函数之间的通信可以通过参数、函数返回值以及外部变量进行。函数可以以任意次序出现在源文件中。源程序可以分成多个文件,只要不把一个函数分在几个文件中就行。关键字extern变量的定义与是声明是有不同的意义。作用域运算符:命名空间std 3-5-1.cpp 3-5-2.cpp3.3 形参与实参形参与实参函数的参数 函数的参数分为形参和实参两种。形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。形参和实参的功能是作数据传送。发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送。函数的形参和实参具有以下特点:1.形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。2.实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使实参获得确定值。3.实参和形参在数量上,类型上,顺序上应严格一致,否则会发生“类型不匹配”的错误。值传递 函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。For example:ch3/3-6.cpp 如何实现函数调用中发生的数据传送是双向的。即修改了形参所指的单元,也就修改了实参。3.4 引用和引用和const引用就是给某一已存在的对象取一别名引用就是给某一已存在的对象取一别名(alias)(alias)。refcount is a reference of an intWhich int to reference exactly?is countas:int count=0;int&refcount=count;refcount=1;/here,count=1 count+;/here,refcount=2;For example:ch3/3-8.cpprules when using references:1.A reference must be initialized when it is created.(Pointers can be initialized at any time.)2.int i;3.int&j;/4.j=i;5.2.Once a reference is initialized to an object,it cannot be changed to refer to another object.(Pointers can be pointed to another object at any time.)6.int i,k;7.int&j=i;8.int&j=k;/9.rules when using references:3.You cannot have NULL references.You must always be able to assume that a reference is connected to a legitimate piece of storage.int a=1;int&refint1=NULL;/References in functions int&fun(int&i)i+;return i;用用constconst表示常量对象表示常量对象#define BUFSIZE 100 BUFSIZE只是一个名字,没有存储空间只是一个名字,没有存储空间问题:宏定义常量没有类型信息;宏定义常量是问题:宏定义常量没有类型信息;宏定义常量是全局的全局的(extern linkage);用宏定义常量编程,调用宏定义常量编程,调试时,不能直接知道它的名字。试时,不能直接知道它的名字。常量处理的老方法常量处理的老方法:C+C+中的常量处理方法中的常量处理方法:const:const的最初动机是的最初动机是取代预处理器取代预处理器definedefine进行值替代进行值替代For example:ch3/3-9.cppconst int bufsize=100;(1)有类型信息;有类型信息;const int i=20;chat chi;/正确正确 /以下程序错:以下程序错:int i;char chi;constconst用于函数参数和返回值用于函数参数和返回值intint f1(const f1(const intint&i)&i)i+;/Illegal-compile-time error,i+;/Illegal-compile-time error,Returning by const value例例1 1:const const intint g()g()return 1;return 1;例例2 2:const char*v()const char*v()return result of function v();return result of function v();3.5 缺省参数值缺省参数值在在C+C+中,函数声明时,可以为一个或多个参中,函数声明时,可以为一个或多个参数指定缺省参数值。数指定缺省参数值。例如:例如:/function definitionvoid delay(int k,int time)/function code body/function declarationvoid delay(int k,int time=300);/function calldelay(5);/等价与等价与delay(5,300);理解要点:理解要点:当函数调用执行时,编译器从左到右顺序将实参与形参结合,当函数调用执行时,编译器从左到右顺序将实参与形参结合,若未指定足够的实参,则编译器按同样顺序用函数声明中的缺若未指定足够的实参,则编译器按同样顺序用函数声明中的缺省值来补足所缺少的实参。省值来补足所缺少的实参。由于对缺省参数的处理是在函数调用表达式执行时进行的,由于对缺省参数的处理是在函数调用表达式执行时进行的,所以,在函数声明中,所以,在函数声明中,“=”后面可以是任意复杂的表达式,后面可以是任意复杂的表达式,甚至是函数调用。如:甚至是函数调用。如:void delay(int k,int time=f(5);/其中其中f(5)是函数调用是函数调用所有带缺省值的参数一律放在函数声明中参数表的最右端。所有带缺省值的参数一律放在函数声明中参数表的最右端。void f(int,int y=1,int z);/因为:函数调用因为:函数调用f(2,4)将产生二义性:将产生二义性:4 y 还是还是 4 z void f(int,int y=1,int z=2);/在函数定义中,不再为参数指定缺省值。在函数定义中,不再为参数指定缺省值。(内联函数除外内联函数除外)3.6 函数重载函数重载一个函数名(标识符)可以被用作多个函数抽一个函数名(标识符)可以被用作多个函数抽象的名字。编译时,根据这些函数的参数个数、象的名字。编译时,根据这些函数的参数个数、参数类型来区分(束定参数类型来区分(束定/绑定绑定binding)binding)For example:unit three:function_overloading.dsw二义性错误二义性错误Outline:要确保不同的参数类型确实不同。要确保不同的参数类型确实不同。例如:例如:typedef float real;float abs(float)/real abs(real)/函数的返回类型不能区分重载函数函数的返回类型不能区分重载函数例如:例如:int process(int)/float process(int)/仅仅用了仅仅用了constconst而使参数类型有所不同,则而使参数类型有所不同,则不能区分。不能区分。例如例如:int process(int)/float process(const int)/Void Complex()apart=0.0;ipart=0.0;Void Complex(double d)apart=d;ipart=0.0;Void Complex(double a,double b)apart=a;ipart=b;Void Complex(double a=0.0,double b=0.0)apart=a;ipart=b;When:Complex();/be equal to Complex(0.0,0.0)Complex(2);/Complex(2,0.0)3.7 内联函数内联函数function call need costs:involved from pushing arguments,making an assembly-language CALL,returning arguments,and performing an assembly-language RETURNFor example:ch3/3-10.cpp 在在C+C+中,用中,用inline functioninline function来替换来替换带参宏的使用。带参宏的使用。C+C+的内联机制和的内联机制和C C中的带中的带参宏定义都是为了获得较高的运行速度。参宏定义都是为了获得较高的运行速度。使用宏和使用宏和内联函数增加了代码空间,但减内联函数增加了代码空间,但减少了程序执行时间少了程序执行时间。因为不再有函数调。因为不再有函数调用开销。但用开销。但C+C+的内联机制比的内联机制比C C中的带参宏中的带参宏定义更安全。定义更安全。#include inline float area(float r)return 3.14*r*r;int main()float f=2;float s=area(f+);coutfendlsendl;return 0;inline function作用:在一个函数定义之前作用:在一个函数定义之前加上关键字加上关键字inline,则称该则称该函数为内联函数。每当在其函数为内联函数。每当在其它程序中有对该内联函数的它程序中有对该内联函数的调用,调用,C+编译器都使用该编译器都使用该内联函数的代码替换该内联内联函数的代码替换该内联函数的调用表达式。函数的调用表达式。使用内联函数的优点:使用内联函数的优点:参数的类型和函数返回值的类型都在函数声明中进行明确的指定。参数的类型和函数返回值的类型都在函数声明中进行明确的指定。这便于编译器发现函数调用中的类型不一致的错误。这便于编译器发现函数调用中的类型不一致的错误。如果传入函数的是表达式,则此表达式仅求值一次。如果传入函数的是表达式,则此表达式仅求值一次。内联函数只宜用于比较简单的函数(不含循环语句和内联函数只宜用于比较简单的函数(不含循环语句和switch语句)。语句)。如果内联函数的体很大,有可能使代码的规模急剧增加。如果内联函数的体很大,有可能使代码的规模急剧增加。编译器如何内联编译器如何内联?64.内联函数和编译器内联函数和编译器 (1)编译器如何实现内联?编译器如何实现内联?(2)内联的局限性内联的局限性:a)假如函数太复杂假如函数太复杂,编译器将不能执行内联编译器将不能执行内联(复杂的函数定义顺序、构造函数和复杂的函数定义顺序、构造函数和析构函数析构函数).b)假如在程序中假如在程序中,隐含或显式取该函数的地址隐含或显式取该函数的地址,则则 c)在程序的在程序的Debug版本版本,不执行内联不执行内联 3.8 编译预处理编译预处理 在前面各章中,已多次使用过以“#”号开头的预处理命令。如包含命令#include,宏定义命令#define等。在源程序中这些命令都放在函数之外,而且一般都放在源文件的前面,它们称为预处理部分。所谓预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理是语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。语言提供了多种预处理功能,如宏定义、文件包含、条件编译等。合理地使用预处理功能编写的程序便于阅读、修改、移植和调试,也有利于模块化程序设计。本章介绍常用的几种预处理功能。无参宏#define PI 3.14159#define NoBall 0#define PlayerBall 1#define ComputerBall 2/无参宏,用符号表示常量,For example:3-9.cpp有参宏和inline(内联)函数#define aera(x)PI*(x)*(x)利用有参宏来替代一些简单的函数调用,为了减少由于调用函数所带来的一些开销.For example:3-10.cpp文件包含#include#include“chess.h 文件包含命令的功能是把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件。在程序设计中,文件包含是很有用的。一个大的程序可以分为多个模块,由多个程序员分别编程。有些公用的符号常量或宏定义等可单独组成一个文件,在其它文件的开头用包含命令包含该文件即可使用。这样,可避免在每个文件开头都去书写那些公用量,从而节省时间,并减少出错。对文件包含命令还要说明以下几点:1.使用尖括号表示在包含文件目录中去查找(包含目录是由用户在设置环境时设置的),而不在源文件目录去查找;使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中去查找。用户编程时可根据自己文件所在的目录来选择某一种命令形式。2.一个include命令只能指定一个被包含文件,若有多个文件要包含,则需用多个include命令。3.文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。条件编译 可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。这对于程序的移植和调试是很有用的。条件编译有三种形式,下面分别介绍:1.第一种形式:#ifdef 标识符 程序段1#else 程序段2#endif Lab and ExerciseLab:实验五
展开阅读全文