1、基于过程的程序设计,雷小锋 13685124458 ,内容纲要,一、基于过程的程序设计概述 二、函数 三、域和生命期 四、函数模板 五、使用泛型,高级话题,三、域和生命期,程序的多文件结构 可以在多个文件之间共享函数和变量 解决办法:函数声明和变量声明 extern 返回类型 函数名(形参表);extern 数据类型 变量名;,三、域和生命期,可以在多个文件之间共享函数和变量 解决办法:函数声明和变量声明,/文件A.cppfloat price = 2.3; /定义void print() coutpriceendl; ,/文件B.cpp extern void print(); extern
2、 float price; int main() price = 3.1;print(); ,三、域和生命期,在定义了一个函数或变量之后,则在 所有的源代码文件中都可以使用它, 只要在使用之前声明一下。,问题:能否得出以下结论,三、域和生命期,客观世界中的任何事物,其存在于一定的时间和空间范围 空间范围:作用域,简称域 时间范围:生命期超出自身的作用域和生命期就不可访问,结论:定义了一个函数或变量之后,则 在所有的源代码文件中都可以使用它, 只要在使用之前声明一下,对吗?,三、域和生命期,作用域:简称域,指标识符能够被使用的空间范围,只有在作用域内标识符才可以被访问(称为可见) 标识符作用域的
3、起点均为标识符说明处生命期:标识符所代表的对象存在的时间范围,三、域和生命期函数,作用域:函数在定义或声明之后,就在其所在代码文件中可见可用 函数具有文件作用域,又称全局域 生命期:函数驻留在代码区,程序开始运行时载入,程序执行结束释放 函数具有与程序运行期等长的寿命,称为具有静态生命期,三、域和生命期变量,(3.1) 变量的作用域 有的变量偏安一隅局部域 有的变量名扬天下全局域 (3.2) 变量的生命期 有的变量寿与天齐静态生命期 有的变量转瞬即逝局部生命期 有的变量命悬人手动态生命期 (3.3) 名字空间namespace,(3.1) 变量的作用域,变量的作用域分析 函数内部的变量只在函数
4、范围内可见 函数外部的变量在文件A.cpp中都可见,/文件A.cpp float price = 2.3; /定义 void print() float price = 3.1;coutpriceendl; ,两种作用域 局部域局部变量 全局域全局变量,(3.1) 变量的作用域局部域,局部域:包括块域和函数声明域 块域 定义:一对大括号括起来的程序段 规则:块中定义的标识符,作用域在块内 举例: 复合语句是一个块;函数是一个块,也称作函数域。函数中定义和声明的标识符,包括形参和局部变量,作用域都在函数内。,【块域举例】,3,5,a=3 b=5,a=5 b=3,输入两数,按从大到小的顺序保存,并
5、输出结果。,结果,栈,t,= 3,int main() int a,b; /具有函数域coutab;cout=a)int t; /具有块域 t=a; a=b; b=t; /交换a,b的值cout“a=“at“b=“bendl;return 0;,上述程序若在最后一个cout语句处增加:couttendl; 则编译时错误,因为t的作用域只在if语句中,其它地方不可见。,【块域举例】,编程完成两个整数的交换。,int main() int a = 10, b = 20;couta“, ” bendl;/要求调用完成后b=10, a=20swap(a, b);couta“, ” bendl;retu
6、rn 0; ,void swap(int a, int b) int temp;temp = a;a = b;b = temp;couta“, ” bendl; ,调用前:a = 10, b = 20,调用前:a = 10, b = 20,调用中:a = 20, b = 10,调用后:a = 10, b = 20,交换失败!原因?,(3.1) 变量的作用域局部域,局部域:包括块域和函数声明域 函数声明域 注意:函数声明与函数定义的区别; 在函数声明时,其中形参作用域只在声明中,即作用域结束于声明的右括号;,由于形参不能被程序的其他地方引用,所以通常只声明形参个数和类型,形参名省略。,(3.1)
7、 变量的作用域全局域,全局域全局变量 又称文件作用域定义在所有函数之外的标识符,其作用域从定义处开始,直到整个文件结束,即全局域文件中定义的全局变量和函数都具有全局域,【全局域举例】,编程完成两个整数的交换。,int a = 10, b = 20; int main() couta“, ” bendl;/要求调用完成后b=10, a=20swap();couta“, ” bendl;return 0; ,void swap() int temp;temp = a;a = b;b = temp;couta“, ” bendl; ,调用前:a = 10, b = 20,调用前:a = 10, b
8、= 20,调用中:a = 20, b = 10,调用后:a = 20, b = 10,分析交换过程!,同名变量的作用域,int n = 0; /全局变量 void print() float price = 3.1;int i = 10;coutntitpriceendl; long i = 20, n;n = price * i;coutntitpriceendl;cout:ntitpriceendl;n = price * i;coutntitpriceendl; ,函数块print中嵌套一个块 其中存在同名变量 试分析输出结果,同名变量的作用域,嵌套块中的同名变量 服从局部优先原则:内层
9、块屏蔽外层块中的同名变量块内局部变量与全局变量同名 块内局部变量优先 块内可通过域运算符“:”访问同名全局变量,(3.1) 变量的作用域小结,局部域局部变量 块域:,如复合语句、函数域 函数声明域 全局域全局变量 所有函数的外部 同名变量的作用域 嵌套块中存在同名局部变量 局部变量与全局变量同名,(3.2) 变量的生命期,变量的生命期:从变量获得存储空间到释放存储空间之间的时段。变量只有在其生命期和作用域中才能被访问,变量的生命期决定于变量位于哪个存储区,寿与天齐,转瞬即逝,命悬人手,(3.2) 变量的生命期,(1) 代码区(Code area):存放程序代码,即程序中各个函数的代码块; (2
10、) 全局数据区(Data area):存放全局数据和静态数据;分配时内存清零,结果变量自动初始化为零; (3) 栈区(Stack area):存放局部变量,如函数中的变量等;分配栈区时不初始化内存,即变量取随机值; (4) 堆区(Free store area):存放与指针相关的动态数据;分配堆区时不处理内存。,(3.2) 变量的生命期,变量的存储类型决定变量位于哪个存储区,从而决定变量的生命期C+提供了四种存储类型说明符: auto、 register :自动存储类型 static:静态存储类型 extern:外部存储类型,(3.2) 变量的生命期自动变量,auto、 register :自
11、动存储类型 用auto说明的变量为自动变量,局部变量都是自动变量。 自动变量位于栈区,具有局部生命期。 自动变量的生命期和作用域是一致的:块开始执行时系统自动分配空间,块执行结束时系统自动释放空间。,为提高程序运行效率,可将某些变量保存在寄存器中,即用register说明为寄存器变量,不提倡使用。,(3.2) 变量的生命期静态变量,static:静态存储类型 用static说明的变量为静态变量 两种静态变量 局部静态变量:定义在局部域的静态变量 全局静态变量:定义在全局域的静态变量 静态变量位于全局数据区,具有静态生命期。若未显式初始化则系统自动将其初始化为全0,且只初始化一次。,(3.2)
12、变量的生命期静态变量,局部静态变量 定义在局部域中的静态变量 当局部块域第一次被执行时,编译系统在全局数据区为局部静态变量分配空间并保存数据,直到整个程序结束才释放该空间 局部静态变量具有局部作用域,但却具有全局生命期。,寿与天齐,影响力有限,【局部静态变量举例】,int st()static int t=100; /局部静态变量t+; return t; int at()int t=100; /自动变量t+; return t; int main()for(int i=0;i5;i+) coutat()t;coutendl;for(i=0;i5;i+) coutst()t;coutendl;
13、return 0; ,运行结果,分析如何得到上述输出?,(3.2) 变量的生命期静态变量,全局静态变量 定义在全局域中的静态变量 全局变量在编译时在全局数据区分配空间,在未显式初始化时系统自动清全0 全局静态变量具有全局作用域和全局生命期,静态全局变量或函数的作用域限制在本文件中,其他文件即使通过extern外部声明也无法访问静态全局变量或函数,(3.2) 变量的生命期外部变量,extern:外部变量 用extern说明的全局变量为外部变量 全局变量和函数缺省为外部的,即其作用域可以延伸到程序的其他文件中 在其他文件中通过使用“extern”外部声明可以共享已定义的全局变量和函数,extern
14、 返回类型 函数名(形参表);extern 数据类型 变量名;,(3.2) 变量的生命期外部变量,问题1:外部变量声明与全局变量定义的区别? 变量定义时编译器为其分配存储空间 变量声明则表示该全局变量已在其他地方定义过,编译系统不再分配存储空间,问题2:若希望限制全局变量或函数只在本文件使用,该如何处理?,/* A.cpp */ void fun2(); /外部函数声明,extern void fun2(); int n; /全局变量定义 int main()n=1;fun2(); / fun2()定义在文件B.cpp中coutn=nendl;return 0; ,尝试分析运行结果,/* B.
15、cpp */ extern int n; /外部变量声明,n定义在A.cpp中 void fun2() n=3; ,【外部存储类型举例】,(3.2) 变量的生命期小结,局部生命期:在局部域定义的变量具有局部生命期;存储于栈区,由系统负责空间的自动分配和释放,且随机初始化;必具有局部作用域。,静态生命期:全局变量、静态变量、函数都具有静态生命期;全局变量和静态变量位于全局数据区,一旦存在就持续到程序运行结束时消亡,且初始化为全0。,动态生命期:变量存储空间的分配和释放由程序控制,位于堆区,不初始化。,(3.3) 名字空间,全局变量和函数的命名必须唯一 同一文件内不唯一,编译错误 多个文件间不唯一
16、,链接错误 故需要在所有程序文件范围内保证唯一 问题一:多人协作编程如何保证唯一性 问题二:使用多家厂商提供的程序库,如MS、IBM、开源代码等,如何保证命名唯一性,(3.3) 名字空间,在test.cpp文件中 使用输出流对象cout输出字符串“Hello” 如果定义全局整型变量cout会如何呢?,/test.cpp #include using namespace std; int cout = 10; int main() cout“Hello”endl; ,error C2872: cout : ambiguous symbol,全局名字冲突问题,解决办法:使用名字空间,(3.3) 名
17、字空间,名字空间 全局名字空间:由全局域引入 用户名字空间:由用户自定义 定义用户名字空间 语法:namespace 空间名 /变量、函数、类、模板;,namespace std / 对象 static ios_base:Init _Ios_init; extern _CRTIMP istream cin; extern _CRTIMP ostream cout; extern _CRTIMP ostream cerr, clog;/ _Winit类 class _CRTIMP _Winit public:_Winit();_Winit(); private:static int _Init_
18、cnt; ;/ 宽字节对象static _Winit _Wios_init; extern _CRTIMP wistream wcin; extern _CRTIMP wostream wcout, wcerr, wclog; ;,【名字空间举例】iostream标准头文件,(3.3) 名字空间,如何使用名字空间中的成员 在名字空间内,直接使用空间内部的标识符 在名字空间外,要通过域操作符:,/test.cpp #include int cout = 10; int main() std:cout“Hello”std:endl; ,OK,(3.3) 名字空间,好麻烦哦,每次都要 std:cou
19、t std:endl std:* 解决办法:通过using曝光,using 名字空间:成员; /曝光该成员 using namespace 名字空间; /曝光所有成员,例如: #include using namespace std;,/test.cpp #include using namespace std; int cout = 10; int main() cout“Hello”endl; ,error C2872: cout : ambiguous symbol,?,/test.cpp #include using std:endl; int cout = 10; int main(
20、) std:cout“Hello”endl; ,(3.3) 名字空间,using namespace std; 曝光了名字空间的所有成员,也增大了名字冲突的可能性,/test.cpp #include using namespace std; int cout = 10; int main() std:cout“Hello”endl; ,(3.3) 名字空间,一点辨疑,#include using namespace std; int main() cout“Hello”endl; ,#include int main() cout“Hello”endl; ,包含则使用C+流类库,该头文件定义
21、在std名字空间中,因此需要using。,包含相当于调用C库,使用的是全局命名空间;,(3.4) 头文件和预编译,头文件是实现声明局部化的重要工具,可以集中放置如下内容: 常量和外部变量声明 函数声明 inline函数定义 用户数据类型 在其他文件中使用或定义对象或函数时,必须包含相应的头文件,#include using namespace std;,(3.4) 头文件和预编译,#include:将头文件包含进用户文件 使头文件中定义的标识符在用户文件中可见,即可使用头文件中标识符和函数 系统定义的头文件 定义一些常用的公用标识符和函数 通常的包含方式:#include 用户定义的头文件 通
22、常的包含方式:#include “*.h”,(3.4) 头文件和预编译,#include是一种预编译指令 指示在预编译处理后,将指令中指明的文件嵌入到当前源程序文件的指令位置处 预编译指令包括: 宏定义指令 文件包含指令 条件编译指令,(3.4) 头文件和预编译,宏定义指令 语法:#define 宏名 常量串 例如:#define PI 3.1416定义宏后,即可在程序中使用标识符PI。在预处理后产生一个中间文件,文件中所有PI被替换为3.1416。,宏替换:字符串和标识符之间的简单替换。预处理不做任何数据类型和合法性检查,也不分配内存单元,(3.4) 头文件和预编译,宏定义可以带参数 语法:
23、#define 宏名(形参表) 表达式串 例如:#define M(x,y) (x)*(y)/2此后,在程序中可以使用M(a,b),预处理后M(a,b)被替换为(a)*(b)/2 参数宏类似于函数,本质却仅是字符替换 参数宏的参数通常要用括号()括起来,#define M(x,y) x*y/2 分析M(8-3, 4+2)预编译后的结果?,文件包含指令: #include,#include指令的两种使用方式: #include 称为标准方式,预处理器将在系统的include子目录下搜索指定文件。适于C+提供的头文件; #include “文件名”预处理器首先在当前文件所在目录下搜索,若找不到再按
24、标准方式搜索。适于用户自定义的头文件。,文件包含指令: #include,嵌套包含:include指令可以嵌套 存在问题:可能导致重复包含,出现标识符重复定义问题,/f1.h enum sexM, F; struct complex ;,/f2.h #include “f1.h” extern sex s1; extern complex c1;,/f3.cpp #include “f1.h” #include “f2.h”;int main() ,例如,error C2011: sex : enum type redefinition error C2011: complex : struc
25、t type redefinition,条件编译指令,解决重复包含:用条件编译指令 条件编译:在不同条件下编译程序的不同部分。 两种条件编译方式 用表达式作编译条件 用宏名作编译条件,条件编译指令,用表达式作编译条件 #if defined #else #endif #if !defined #else #endif 用宏名作编译条件 #ifdef #else #endif #ifndef #else #endif #ifdef #elif #elif #else #endif,条件编译指令,#ifdef, #ifndef, defined用来测试某个宏是否被定义 例如,#if defined
26、(UNIX) & !defined(DEBUG) #include “f1.h“ #endif,#ifndef _FILE_H_ #define _FILE_H_ #include “f1.h“ #endif,/f1.h #ifndef _FILE1_H_ #define _FILE1_H_ enum sexM, F; struct complex double real;double img; ; #endif,/f2.h #include “f1.h” extern complex add(complex,/f3.cpp #include “f1.h” #include “f2.h”;int main() ,/f2.cpp #include “f2.h” ,三、域和生命期小结,域和生命期的概念 函数的域和生命期 变量的域和生命期 局部域/局部变量、全局域/全局变量 变量存储区域: 全局数据区、堆区、栈区 变量存储类型: auto/register,static,extern 变量生命期:局部、静态、动态 名字空间及其using 头文件和预编译,