56

Click here to load reader

第五章 函数与编译预处理

  • Upload
    charo

  • View
    142

  • Download
    14

Embed Size (px)

DESCRIPTION

第五章 函数与编译预处理. 河海大学计算机信息学院 丁海军 [email protected]. 第六周上机题目 P96 2 , 4 , 5 , 6 , 12 第七周上机韪 P96 13 , 14 , 15 P101  例 6.3 P102  例 6.4 P105 例 6.5 P107 例 6.7, 例 6.8. 概述. 一 、模块化程序设计 基本思想:将一个大的程序按功能分割成一些小模块 , 作用: 各模块相对独立、功能单一、结构清晰、接口简单 控制了程序设计的复杂性 提高元件的可靠性 缩短开发周期 - PowerPoint PPT Presentation

Citation preview

Page 1: 第五章 函数与编译预处理

1

第五章第五章函数与编译预处理函数与编译预处理

河海大学计算机信息学院 丁海军河海大学计算机信息学院 丁海军[email protected]@webmail.hhuc.edu.cn

Page 2: 第五章 函数与编译预处理

2

第六周上机题目第六周上机题目P96P96    22 ,, 44 ,, 55 ,, 66 ,, 1212   第七周上机韪第七周上机韪P96P96    1313 ,, 1414 ,, 1515P101P101  例 例 6.36.3P102P102  例 例 6.46.4P105 P105 例例 6.56.5P107 P107 例例 6.7,6.7, 例例 6.86.8

Page 3: 第五章 函数与编译预处理

3

概述概述一一、模块化程序设计、模块化程序设计

基本思想:将一个大的程序按功能分割成一些基本思想:将一个大的程序按功能分割成一些小模块小模块 ,,

作用:作用:各模块相对独立、功能单一、结构清晰、接口简单各模块相对独立、功能单一、结构清晰、接口简单控制了程序设计的复杂性控制了程序设计的复杂性提高元件的可靠性提高元件的可靠性缩短开发周期缩短开发周期避免程序开发的避免程序开发的重复劳动重复劳动易于维护和功能扩充易于维护和功能扩充

开发方法开发方法 : : 自上向下自上向下 ,, 逐步分解,分而治之逐步分解,分而治之

Page 4: 第五章 函数与编译预处理

4

C++ 是函数式语言必须有且只能有一个名为 main 的主函数C++ 程序的执行总是从 main 函数开始,在 main 中结束函数不能嵌套定义 ,可以嵌套调用

概述概述

二、复习

Page 5: 第五章 函数与编译预处理

5

二、函数分类二、函数分类从用户角度从用户角度

标准函数(库函数):由系统提供标准函数(库函数):由系统提供用户自定义函数用户自定义函数

从函数形式从函数形式无参函数无参函数有参函数有参函数

使用库函数应注意:1 、函数功能2 、函数参数的数目和顺序,及各参数意义和类型3 、函数返回值意义和类型4 、需要使用的包含文件

概述概述

Page 6: 第五章 函数与编译预处理

6

例例 :: 从键盘输入一个整数从键盘输入一个整数 nn ,按下式计算,按下式计算 yy 的值,的值,并输出并输出 yy 和和 n n 的值(的值( yy 用实数表示)用实数表示)

y=1!+2!+3!+…+n!y=1!+2!+3!+…+n!分析分析(1) (1) 输入整数输入整数 n n cin>>ncin>>n

(2)(2)求整数求整数 nn 的阶乘和的阶乘和 yy

(3) (3) 输出输出 yy 和和 nn 的值的值 coucout<<y<<nt<<y<<n

void main()void main()

{{

int n,i,j;int n,i,j;

double y;double y;

cin>>n;cin>>n;

y=0y=0

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

{{

factorial_i=<ifactorial_i=<i 的阶乘的阶乘 >;>;

y=y+factorial_i;y=y+factorial_i;

}}

cout<<“y=“<<y<<endl;cout<<“y=“<<y<<endl;

cout<<“n=“<<n<<endlcout<<“n=“<<n<<endl

}}

Factorial_i=1;Factorial_i=1;

for(j=1;j<=I;j++)for(j=1;j<=I;j++)

    factorial_i=factorial_i*factorial_i=factorial_i*j;j;

5.15.1 从一个编程任务谈起从一个编程任务谈起 (P60(P60 第第 88 题题 ))

Page 7: 第五章 函数与编译预处理

7

这是一个完整的程序,是这是一个完整的程序,是可以运行的,但存在一些可以运行的,但存在一些问题:问题:(( 11 )整个程序看起来结)整个程序看起来结构不是太清晰构不是太清晰(( 22 )求一个数的阶乘是)求一个数的阶乘是一个很通用的功能,很多一个很通用的功能,很多地方要使用,应该将其作地方要使用,应该将其作为一个公用的函数存在。为一个公用的函数存在。

5.15.1 概述概述#include<iostream.h>#include<iostream.h>void main()void main(){{int n,i,j;int n,i,j;long y,factorial_i;long y,factorial_i;cin>>n;cin>>n;y=0;y=0;for(i=1;i<=n;i++)for(i=1;i<=n;i++) {{ factorial_i=1;factorial_i=1; for(j=1;j<=i;j++)for(j=1;j<=i;j++) factorial_i=factorial_i*j; factorial_i=factorial_i*j; y=y+factorial_i;y=y+factorial_i; }}cout<<"y="<<y<<endl;cout<<"y="<<y<<endl;cout<<"n="<<n<<endl;cout<<"n="<<n<<endl;}}

Page 8: 第五章 函数与编译预处理

8

5.15.1 概述概述//// 主程序主程序#include<iostream.h>#include<iostream.h>double factorial(int i);double factorial(int i);void main()void main(){{ int n,i,j;int n,i,j; long y,factorial_i;long y,factorial_i; cin>>n;cin>>n; y=0;y=0; for(i=1;i<=n;i++)for(i=1;i<=n;i++) {{ factorial_i=fac (i);factorial_i=fac (i);    y=y+factorial_i;y=y+factorial_i; }} cout<<"n="<<n<<endl;cout<<"n="<<n<<endl; cout<<"y="<<y<<endl;cout<<"y="<<y<<endl;}}

//// 求阶乘子程序求阶乘子程序long fac (int i)long fac (int i){{ long x;long x; x=1;x=1; for(j=1;i<=I;j++)for(j=1;i<=I;j++) x=x*j;x=x*j;   return x;return x;   }}

函数定义函数定义

语法要素语法要素函数的定义要素函数的定义要素函数调用要素函数调用要素函数的参数传递函数的参数传递变量存储类型变量存储类型

Page 9: 第五章 函数与编译预处理

9

5.2 5.2 函数定义和调用函数定义和调用5.2.1 5.2.1 函数的定义函数的定义5.2.2 5.2.2 函数的返回值函数的返回值5.2.3 5.2.3 函数的调用函数的调用5.2.4 5.2.4 函数的原型说明函数的原型说明

Page 10: 第五章 函数与编译预处理

10

5.2.1 5.2.1 函数定义函数定义一.一.一般格式一般格式 合法标识符

函数体函数类型 函数名(形参类型说明表){

说明部分语句部分

}

函数定义风格 :

例 有参函数(现代风格) int max(int x,int y) { int z; z=x>y?x:y; return(z); }

例 有参函数 ( 错误在哪儿? ) int max(int x, y) { int z; z=x>y?x:y; return(z); }

例 空函数 dummy( ) { }

函数体为空

例 无参函数 printstar( ) { cout<<“**********\n”; }或 printstar(void ) { cout<<“**********\n”; }

函数返回值类型缺省 int 型

无返回值 void

//// 求阶乘子程序求阶乘子程序long factorial(int i)long factorial(int i){{long x;long x;x=1;x=1;for(j=1;i<=I;j++)for(j=1;i<=I;j++) x=x*j;x=x*j;return x;return x;}}

Page 11: 第五章 函数与编译预处理

11

5.2.2 5.2.2 函数的返回值函数的返回值返回语句返回语句

形式: 形式: return(return( 表达式表达式 )) ;; 或或 return return 表达式表达式 ;;

或或 return;return;功能:使程序控制从被调用函数返回到调用函数中,功能:使程序控制从被调用函数返回到调用函数中,

同时把返值带给调用函数同时把返值带给调用函数说明:说明:

函数中可有多个函数中可有多个 returnreturn 语句语句若无若无 returnreturn 语句,遇语句,遇 }} 时,自动返回调用函数时,自动返回调用函数若函数类型与若函数类型与 returnreturn 语句中表达式值的类型不语句中表达式值的类型不

一致,按前者为准,自动转换一致,按前者为准,自动转换 ------------ 函数调用转函数调用转换换

voidvoid 型函数,无返回值,使用 型函数,无返回值,使用 returnreturn

例 无返回值函数 void swap(int &x,int &y ) { int temp; temp=x; x=y; y=temp; }

return;

y=y=

=1 x>0 =1 x>0

=0 x=0=0 x=0

=-1 x<0=-1 x<0

例:符号函数例:符号函数 (( 多返回语多返回语句句 ))

int sign(int x)int sign(int x){{ if (x>0) if (x>0) return 1return 1;; if (x= =0) if (x= =0) return 0return 0;; if (x<0) if (x<0) return -1return -1;;}}

Page 12: 第五章 函数与编译预处理

12

printstar(){ cout<<"**********";}main(){ int a; a=printstar(); cout<<a;}

例 函数带回不确定值

输出: 10

void printstar(){ printf("**********");}main(){ int a; a=printstar(); cout<<a;}

编译错误!

5.2.2 5.2.2 函数的返回值函数的返回值

Page 13: 第五章 函数与编译预处理

13

例 函数返回值类型转换 main(){ float a,b,c; cin>>a>>b; c=max(a,b); cout<<"Max is “<<c<<endl;}/////////////////////////////////int max(float x, float y){ float z; z=x>y?x:y; return(z);}

输入 5.5   2.3

运行结果:

5.0

5.2.2 5.2.2 函数的返回值函数的返回值

Page 14: 第五章 函数与编译预处理

14

5.2 3 5.2 3 函数的调用函数的调用调用形式调用形式 函数名函数名 (( 实参表实参表 ););

说明:说明: 实参与形参实参与形参个数相等个数相等,,类型一致类型一致,,按顺按顺

序一一对应序一一对应 实参表求值顺序,因系统而定(一般实参表求值顺序,因系统而定(一般自自

右向左右向左))

Page 15: 第五章 函数与编译预处理

15

#include<iostream.h>int fun(int a, int b);main(){ int i=2,p; p=fun(i,++i); cout<<p;}

int fun(int a, int b){ int c; if(a>b) c=1; else if(a==b) c=0; else c=-1; return(c);}

例 参数求值顺序

int fun(int a, int b);main(){ int i=2,p; p=fun(i, i++); cout<<p);}

int fun(int a, int b){ int c; if(a>b) c=1; else if(a==b) c=0; else c=-1; return(c);}

运行结果: 0

运行结果: 0

5.2 3 5.2 3 函数的调用函数的调用

Page 16: 第五章 函数与编译预处理

16

调用方式调用方式函数作为一条独立语句:函数作为一条独立语句: 例 例 {printstar();{printstar(); cout<<“Hello,World!\n”;}cout<<“Hello,World!\n”;}函数作为表达式中的一个操作数:函数作为表达式中的一个操作数: //// 必须是有返回值必须是有返回值

的函数的函数 例 例 { m=max(a,b)*2;}{ m=max(a,b)*2;}函数作为加一个函数的参数:函数作为加一个函数的参数: 例 例 {{ cout<<max(a,b);cout<<max(a,b); m=max(a,max(b,c));m=max(a,max(b,c)); }}

5.2 3 5.2 3 函数的调用函数的调用

Page 17: 第五章 函数与编译预处理

17

5.2.4 5.2.4 函数的原型说明函数的原型说明

对被调用函数要求对被调用函数要求必须是必须是已存在已存在的函数的函数库函数库函数 : : #include <*.h>#include <*.h>用户自定义函数用户自定义函数 : : 函数原型说明函数原型说明

函数原型说明函数原型说明一般形式: 一般形式: 函数类型 函数名函数类型 函数名 (( 形参类型 形参类型 [[ 形参名形参名 ],….. );],….. );

作用:告诉编译系统函数类型、参数个数及类型,作用:告诉编译系统函数类型、参数个数及类型,以便检验以便检验

函数定义函数定义与与函数说明函数说明不同不同函数说明位置:函数说明位置:程序的数据说明部分(函数内或程序的数据说明部分(函数内或

外)外)

程序员素质:

对任何被调用函数都先作说明

Page 18: 第五章 函数与编译预处理

18

例 函数说明举例

float add(float,float); //function declarationmain(){ float a,b,c; cin>>a>>b; c=add(a,b); cout<<"sum is “<<c;}float add(float x, float y){ float z; z=x+y; return(z);}

5.2.4 5.2.4 函数的原型说明函数的原型说明

Page 19: 第五章 函数与编译预处理

19

5.3 5.3 函数参数及其传递方式函数参数及其传递方式形参与实参形参与实参形式参数:定义函数时函数名后面括号中的变量名形式参数:定义函数时函数名后面括号中的变量名实际参数:调用函数时函数名后面括号中的表达式实际参数:调用函数时函数名后面括号中的表达式

c=max(a,b); ( main 函数) ( max 函数)int max(int x, int y)

{ int z; z=x>y?x:y; return(z);}

main(){ int a,b,c; cin>>a>>b; c=max(a,b); cout<<"Max is “<<c;}int max(int x, int y){ int z; z=x>y?x:y; return(z);}

形参

实参

Page 20: 第五章 函数与编译预处理

20

5.3 5.3 函数参数及其传递方式函数参数及其传递方式

形参与实参形参与实参说明:说明:实参必须有确定的值实参必须有确定的值形参必须指定类型形参必须指定类型形参与实参类型一致,个数相同形参与实参类型一致,个数相同若形参与实参类型不一致,自动按形参类型转换若形参与实参类型不一致,自动按形参类型转换

————函数调用转换函数调用转换形参在函数被调用前不占内存形参在函数被调用前不占内存 ;; 函数调用时为形函数调用时为形

参分配内存;调用结束,内存释放参分配内存;调用结束,内存释放

Page 21: 第五章 函数与编译预处理

21

5.3 5.3 函数参数及其传递方式函数参数及其传递方式值传递方式值传递方式

方式:函数调用时方式:函数调用时 ,, 为形参分配单元为形参分配单元 ,, 并将实参的值并将实参的值复制到形参中;调用结束,形参单元被释放,实参复制到形参中;调用结束,形参单元被释放,实参单元仍保留并维持原值单元仍保留并维持原值

特点:特点:形参与实参占用不同的内存单元形参与实参占用不同的内存单元单向传递单向传递

Page 22: 第五章 函数与编译预处理

22

7 11x: y:调用前:

调用结束: 7 11x: y:

例 交换两个数#include<iostream.h>void swap(int a,int b);main(){ int x=7,y=11; cout<<"==original==\n" ; cout<<"x="<<x<<"\t" ; cout<<"y="<<y<<"\n" ; swap(x,y); cout<<"==in main==\n"; cout<<"x="<<x<<"\t" ; cout<<"y="<<y<<"\n" ; cin>>x;}void swap(int a,int b){ int temp; temp=a; a=b; b=temp; cout<<"==in swap==\n"; cout<<"a="<<a<<" b="<<b<<endl;}

调用:7 11a: b:

7 11x: y:

swap: 7 11x: y:

11 7a: b:

temp

==original====original==x=7 y=11x=7 y=11==in swap====in swap==a=11 b=7a=11 b=7==in main====in main==x=7 y=11x=7 y=11

Page 23: 第五章 函数与编译预处理

23

5.3 5.3 函数参数及其传递方式函数参数及其传递方式地址传递地址传递

方式:函数调用时,将数据的方式:函数调用时,将数据的存储地址存储地址作为作为参数传递给形参参数传递给形参

特点:特点:形参与实参占用形参与实参占用同样同样的存储单元的存储单元““双向”双向”传递传递实参和形参必须是实参和形参必须是地址地址常量或变量常量或变量

Page 24: 第五章 函数与编译预处理

24

例 交换两个数

==original====original==x=7 y=11x=7 y=11==in swap====in swap==a=11 b=7a=11 b=7==in main====in main==x=11 y=7x=11 y=7

5.3 5.3 函数参数及其传递方式函数参数及其传递方式/*ch7_2.c*/#include<iostream.h>void swap(int &a,int &b);main(){ int x=7,y=11; cout<<"==original==\n" ; cout<<"x="<<x<<"\t" ; cout<<"y="<<y<<"\n" ; swap(x,y); cout<<"==in main==\n"; cout<<"x="<<x<<"\t" ; cout<<"y="<<y<<"\n" ; cin>>x;}swap(int &a,int &b){ int temp; temp=a; a=b; b=temp; cout<<"==in swap==\n"; cout<<"a="<<a<<" b="<<b<<endl;}

Page 25: 第五章 函数与编译预处理

25

#include <iostream.h> long sum(int a, int b); long factorial(int n);main() { int n1,n2; long a; cin>>n1>>n2; a=sum(n1,n2); cout<<"a=“<<a; }long sum(int a,int b){ long c1,c2; c1=factorial(a); c2=factorial(b); return(c1+c2);}

long factorial(int n) { long rtn=1; int i; for(i=1;i<=n;i++) rtn*=i; return(rtn); }

文件包含编译预处理命令

函数类型说明

函数定义

函数调用

函数调用

函数返回值

形参

实参

总结总结

Page 26: 第五章 函数与编译预处理

26

C++C++ 规定:规定:函数定义不可嵌套函数定义不可嵌套,但,但可以嵌套可以嵌套调用调用函数函数

5.35.3 函数的嵌套调用函数的嵌套调用

例 求三个数中最大数和最小数的差值

Page 27: 第五章 函数与编译预处理

27

#include <iostream.h> int dif(int x,int y,int z); int max(int x,int y,int z); int min(int x,int y,int z);void main() { int a,b,c,d; cin>>a>>b>>c; d=dif(a,b,c); cout<<"Max-Min=“<<d; }

int dif(int x,int y,int z){ return max(x,y,z)-min(x,y,z); }

int max(int x,int y,int z) { int r; r=x>y?x:y; return(r>z?r:z); }

int min(int x,int y,int z) { int r; r=x<y?x:y; return(r<z?r:z); }

main( )

调用函数 dif

输出结束

dif 函数 max 函数

调用函数 max调用函数 min min 函数

Page 28: 第五章 函数与编译预处理

28

5.4 5.4 变量的存储属性变量的存储属性基本概念:基本概念:变量是对程序中数据的存储空间的抽象变量是对程序中数据的存储空间的抽象变量的属性变量的属性

数据类型:变量所持有的数据的性质(数据类型:变量所持有的数据的性质(操作属性操作属性))存储类型存储类型 ::决定变量的作用域和生存期(存储属性)决定变量的作用域和生存期(存储属性)

变量定义格式变量定义格式 : : [[ 存储类型存储类型 ] ] 数据类型 变数据类型 变量表量表 ;; 存储器类型:寄存器、静态存储区、动存储器类型:寄存器、静态存储区、动

态存储区态存储区生存期生存期:变量在某一时刻存在:变量在某一时刻存在 -------------- 静静态变量态变量与与动态变量动态变量作用域作用域:变量在某区域内有效:变量在某区域内有效 -------------- 局局部变量部变量与与全局变量全局变量

与存储类型有关的关键字与存储类型有关的关键字①① auto -----auto ----- 自动型自动型②② register-----register----- 寄存器型寄存器型③③ static ------static ------ 静态型静态型④④ extern -----extern ----- 外部型外部型

Page 29: 第五章 函数与编译预处理

29

5.4 5.4 变量的存储属性变量的存储属性变量在程序中的定义位置变量在程序中的定义位置

变量在内存中的存储位置变量在内存中的存储位置

变量的生存期变量的生存期

变量的存储修饰符 (static)

全局作用域全局作用域局部作用域局部作用域

函数作用域函数作用域块作用哉块作用哉

作用域作用域

静态存储静态存储动态存储动态存储存储类型存储类型

代码段代码段 数据段数据段 栈段栈段内存分布

Page 30: 第五章 函数与编译预处理

30

   特征   特征位置位置

无无 staticstatic 修饰修饰存储位置存储位置 叫法叫法 作用域作用域 生存期生存期

所有函数之所有函数之外外

数据段数据段 全局变量全局变量 所有文件所有文件 程序执程序执行期行期

函数定义的函数定义的一对一对 {{ 、、 }}之内,块语之内,块语句之外句之外

栈段 栈段  局部变量局部变量 变量定义变量定义所在函数所在函数

函数执函数执行期行期

块语句块语句 {{ 、、 }}之内之内

栈段栈段 局部变量局部变量 变量定义变量定义所在块所在块

块执行块执行期期

5.4 5.4 变量的存储属性变量的存储属性

Page 31: 第五章 函数与编译预处理

31

   特征   特征位置位置

有有 staticstatic 修饰修饰

存储位置存储位置 叫法叫法 作用域作用域 生存期生存期

所有函数之所有函数之外外

数据段数据段 静态全静态全局变量局变量

所有文件所有文件 程序执程序执行期行期

函数定义的函数定义的一对一对 {}{} 之内,之内,块语句之外块语句之外

数据段 数据段  静态局静态局部变量部变量

变量定义所在变量定义所在函数函数

程序执程序执行期行期

块语句块语句 {}{} 之之内内

数据段数据段 静态局静态局部变量部变量

变量定义所在变量定义所在块块

程序执程序执行期行期

5.4 5.4 变量的存储属性变量的存储属性

Page 32: 第五章 函数与编译预处理

32

例 文件 file1.cint a;main( ){ ……. ……. f2; ……. f1; …….}f1( ){   int b; ……… f2; ……..}f2( ){ static int c; ………}

C 作用域

b 作用域

a 作用域main f2 f1main f1f2 main

a 生存期 :

b 生存期 :

c 生存期 :

Page 33: 第五章 函数与编译预处理

33

5.4 5.4 变量的存储属性变量的存储属性说明:说明:

①① mainmain 中定义的变量只在中定义的变量只在 mainmain 中有效中有效②② 不同位置的同名变量,占不同内存单元 不同位置的同名变量,占不同内存单元 ③③ (( 因此是不同的变量因此是不同的变量 ))

④④ 形参属于局部变量形参属于局部变量⑤⑤ 可用于局部变量修饰符可用于局部变量修饰符:: auto register static auto register static

(默认为(默认为 autoauto , , registerregister 很少用)很少用)⑥⑥ 变量必须先定义才能使用变量必须先定义才能使用⑦⑦ 全局变量如要在多个文件中使用,需要用全局变量如要在多个文件中使用,需要用 externextern修饰符修饰修饰符修饰

Page 34: 第五章 函数与编译预处理

34

5.4 5.4 变量的存储属性变量的存储属性float f1(int a) { int b,c; …….}

char f2(int x,int y){ int i,j; ……}

main(){ int m,n; …….}

a,b,c 有效

x,y,i,j 有效

m,n 有效

例 不同函数中同名变量

main(){ int a,b; a=3; b=4; cout<<"main:a=“<<a<<“ b=“<<b;   sub(); cout<<"main:a=“<<a<<“ b=“<<b;}///////////////////////////////sub(){ int a,b; a=6; b=7; cout<<“sub:a=“<<a<<“ b=“<<b; }

运行结果:main:a=3,b=4sub:a=6,b=7main:a=3,b=4

Page 35: 第五章 函数与编译预处理

35

5.4 5.4 变量的存储属性变量的存储属性#include<iostream.h>#include<iostream.h>int x=10;int x=10;int main()int main(){ int x=100;{ int x=100; for(int i=1;i<=10;i++)for(int i=1;i<=10;i++) {{ int j=10;int j=10; j=j*i;j=j*i; }} cout<<x; cout<<x; x=100x=100 cout<<i<<j; cout<<i<<j;   }}

int a=3,b=5;max(int a, int b){ int c; c=a>b?a:b; return(c);}main(){ int a=8; cout<<"max=“<<max(a,b);}

运行结果: max=8

编译错误编译错误 !!

Page 36: 第五章 函数与编译预处理

36

#include<iostream.h>void prt();int i;main(){ for(i=0;i<5;i++) prt();}///////////////////////void prt(){ for(i=0;i<5;i++)    cout<<'*';  cout<<endl;}

例 全局变量的副作用

运行结果:*****

编写程序显示如下的图形:*************************

#include<iostream.h>void prt();main(){  int i; for(i=0;i<5;i++) prt(); cin>>i;}///////////////////////void prt(){ int i; for(i=0;i<5;i++) cout<<'*'; cout<<endl;}

?!!!

5.4 5.4 变量的存储属性变量的存储属性

Page 37: 第五章 函数与编译预处理

37

5.4 5.4 变量的存储属性变量的存储属性 变量存储类型总结变量存储类型总结

静态动态存储方式

程序整个运行期间函数调用开始至结束生存期

编译时赋初值,只赋一次每次函数调用时赋初值自动赋初值 0 或空字符不确定未赋初值

静态存储区动态区存储区 寄存器

局部变量 全局变量

作用域 定义变量的函数或复合语句内 本文件 其它文件

局部变量默认为 auto 型register 型变量个数受限 ,且不能为 long, double, float 型局部 static 变量具有全局寿命和局部可见性局部 static 变量具有可继承性extern 不是变量定义 ,可扩展外部变量作用域

register 局部 staticauto 全局 static 全局存储类别

Page 38: 第五章 函数与编译预处理

38

void increment(void);main(){ increment(); increment(); increment();}void increment(void){ int x=0; x++; cout<<x;}

例 局部静态变量值具有可继承性

运行结果: 1 1 1

void increment(void);main(){ increment(); increment(); increment();}void increment(void){ static int x=0; x++; cout<<x;}

运行结果: 1 2 3

Page 39: 第五章 函数与编译预处理

39

main(){ void gx(),gy(); extern int x,y; cout<<"1: x="<<x<<"\ty="<<y<<endl; y=246; gx(); gy();}

void gx(){ extern int x,y; x=135; cout<<"1: x="<<x<<"\ty="<<y<<endl;}

int x,y;void gy(){ cout<<"1: x="<<x<<"\ty="<<y<<endl;}

例 用 extern 扩展外部变量作用域

运行结果:1: x=0 y=02: x=135 y=2463: x=135 y=246

Page 40: 第五章 函数与编译预处理

40

例 引用其它文件中的变量,输出 ab 和 a 的 m 次方

//xx.cppint a;main(){ int power(int n); int b=3,c,d,m; cout<<"Enter the number a and its power:\n"; cin>>a>>m; c=a*b; cout<<a<<b<<c; d=power(m); cout<<a<<“^”<<m<<“=“<<d;}

//x1.cppextern int a;int power(int n){ int i,y=1; for(i=1;i<=n;i++)

y*=a; return(y);}

Page 41: 第五章 函数与编译预处理

41

阅读下面程序,预测程序结果阅读下面程序,预测程序结果 . . #include<iostream.h>#include<iostream.h> void main( )void main( ) { int a = 5, b = 7, c = 10;{ int a = 5, b = 7, c = 10; cout<<a<<cout<<a<<””,,””<<b<<<<b<<””,,””<<c<<endl;<<c<<endl; { int b = 8;{ int b = 8; double c = 8.8;double c = 8.8; cout<<a<<cout<<a<<””,,””<<b<<<<b<<””,,””<<c<<endl;<<c<<endl; a = b;a = b; { int c = b;{ int c = b; cout<<a<<cout<<a<<””,,””<<b<<<<b<<””,,””<<c<<endl;<<c<<endl; }} cout<<a<<cout<<a<<””,,””<<b<<<<b<<””,,””<<c<<endl;<<c<<endl; }} cout<<a<<cout<<a<<””,,””<<b<<<<b<<””,,””<<c<<endl;<<c<<endl; }}

5,7,105,7,105,8,8.85,8,8.88,8,88,8,88,8,8.88,8,8.88,7,108,7,10

Page 42: 第五章 函数与编译预处理

42

5.55.5 内联函数内联函数内联函数也称为内嵌函数,解决程序的运行内联函数也称为内嵌函数,解决程序的运行效率效率函数调用需要建立调用栈环境,进行参数传函数调用需要建立调用栈环境,进行参数传递,产生程序执行的转移,这些工作需要时递,产生程序执行的转移,这些工作需要时间开销间开销有些函数使用频率高,但代码很短有些函数使用频率高,但代码很短

float max(float x,float y);

void main()

{float A,B;

cout<<Input A and B”;

cin>>A>>B;

cout<<” 最大值=“ <<max(A,B)<<endl;

}float max(float x,float y)

{

  return (x>y? x: y);

}

inline float max(float x,float y)

{

  return (x>y? x: y);

}

inline float max(float x,float y);

void main()

{float A,B;

cout<<Input A and B”;

cin>>A>>B;

cout<<” 最大值=“ <<max(A,B)<<endl;

}

特别注意:特别注意:循环语句循环语句SwitchSwitch 语句语句复杂嵌套的 复杂嵌套的 if~else if~else 语句语句

出现在内联函数中无效出现在内联函数中无效

Page 43: 第五章 函数与编译预处理

43

5.6 5.6 具有缺省参数值的函数具有缺省参数值的函数#include<iostream.h>#include<iostream.h>

void delay(int n=1000)void delay(int n=1000)

{ for( ; n>0; n- -);{ for( ; n>0; n- -);

}}

void main()void main()

{ cout<<"{ cout<<" 延时延时 500500 个时间单位…个时间单位… \\n";n";

delay(500);delay(500);

cout<<"cout<<" 延时延时 10001000 个时间单位…个时间单位… \\n";n";

delay();delay();

} }

P81-83

#include<iostream.h>#include<iostream.h>

void delay(int n=200);void delay(int n=200);

void main()void main()

{ cout<<"{ cout<<"延时延时 500500 个时间单位…个时间单位… \\n";n";

delay(500);delay(500);

cout<<"cout<<"延时延时 200200 个时间单位…个时间单位… \n";\n";

delay();delay();

}}

void delay(int n)void delay(int n)

{ for( ; n>0; n- -);{ for( ; n>0; n- -);

} }

Page 44: 第五章 函数与编译预处理

44

5.7 5.7 函数重载 函数重载 在在 CC 语言中,仅求绝对值的函数就有以下几个语言中,仅求绝对值的函数就有以下几个     int abs(int x)int abs(int x)

     long labs(long x)long labs(long x)

     float fabs(float x)float fabs(float x)

这显然不利于人们记忆,为了求一个绝对值,这显然不利于人们记忆,为了求一个绝对值,人们要记忆三个函数及其参数要求。人们要记忆三个函数及其参数要求。

函数重载函数重载

Page 45: 第五章 函数与编译预处理

45

5.7 5.7 函数重载 函数重载 //// 重新定义绝对值函数重新定义绝对值函数int abs(int x) //int abs(int x) // 整形整形{ return (x<0? -x:x);}{ return (x<0? -x:x);}

long abs(long x)//long abs(long x)// 长整形 长整形 { return (x<0? -x:x);}{ return (x<0? -x:x);}

double abs(double x)//double abs(double x)// 浮点浮点{ return (x<0? -x:x);}{ return (x<0? -x:x);}

//// 主程序主程序Main()Main()

{ int x1=10;{ int x1=10;

long x2=-53456;long x2=-53456;

float x3=-153.5float x3=-153.5

cout<<abs(x1)<<endl;cout<<abs(x1)<<endl;

cout<<abs(x2)<<endl;cout<<abs(x2)<<endl;

cout<<abs(x3)<<endl;cout<<abs(x3)<<endl;

}}

说明说明定义的重载函数必须具有不同定义的重载函数必须具有不同

的参数类型或参数个数的参数类型或参数个数仅返回值不同时,不能定义为仅返回值不同时,不能定义为

重载函数重载函数

Page 46: 第五章 函数与编译预处理

46

5.8 5.8 编译预处理编译预处理作用:对源程序编译之前做一些处理作用:对源程序编译之前做一些处理 ,, 生成生成扩展扩展 CC 源程序源程序种类种类文件包含 文件包含 #include#include宏定义 宏定义 #define#define条件编译 条件编译 #if--#else--#endif#if--#else--#endif 等(一般不用)等(一般不用)格式:格式:““#”#” 开头开头占单独书写行占单独书写行语句尾不加分号语句尾不加分号

5.8.1 5.8.1 包含文件包含文件 ((头文件头文件 ))5.8.2 5.8.2 宏定义宏定义

Page 47: 第五章 函数与编译预处理

47

5.8.1 5.8.1 包含文件包含文件 ((头文件头文件 ))

功能:一个源文件可将另一个源文件的内容全功能:一个源文件可将另一个源文件的内容全部包含进来部包含进来

一般形式: 一般形式: #include “#include “ 文件名”文件名” 或 或 #include <#include < 文件名文件名 >>处理过程:预编译时处理过程:预编译时 ,, 用被包含文件的内容取用被包含文件的内容取代该预处理命令,再对“包含”后的文件作一代该预处理命令,再对“包含”后的文件作一个源文件编译个源文件编译

< >< > 直接按标准目录搜索直接按标准目录搜索" "" " 先在先在当前目录当前目录搜索,再搜索标准目录搜索,再搜索标准目录可指定路径可指定路径

#include “file2.c”

file1.cpp

A

file2.cpp

Bfile2.c

A

file1.cpp

说明:说明:①① 理论上讲,#理论上讲,# includeinclude 后面可以包含任可符合后面可以包含任可符合 CC+++语法规范的程序语句。+语法规范的程序语句。

②② 实际使用时我们只建议将函数原型说明,常数值定实际使用时我们只建议将函数原型说明,常数值定义及其它的一些宏定义放在被包含文件中,对这样义及其它的一些宏定义放在被包含文件中,对这样的文件我们称为头文件的文件我们称为头文件

Page 48: 第五章 函数与编译预处理

48

说明:说明:• 宏定义功能#宏定义功能# definedefine

是从是从 CC 语言继承过来语言继承过来,建议大家少使用。,建议大家少使用。

• 宏定义主要用于定义宏定义主要用于定义一些符号常数,建议一些符号常数,建议大家使用关键字大家使用关键字 conconstst 来定义常数来定义常数

5.8.2 5.8.2 宏定义宏定义一、不带参数的宏定义一、不带参数的宏定义语法:语法:## definedefine 宏名 表达式 宏名 表达式 

例:例:#define#define PI 3.141592 PI 3.141592

#define#define PROMPT " PROMPT " 面积面积为:为: ""

#define#define AREA PI*r*r AREA PI*r*r

例:宏定义的使用例:宏定义的使用## include<iostream.h>include<iostream.h>#define PI 3.141592#define PI 3.141592#define R=2.8#define R=2.8#define AREA PI*R*R#define AREA PI*R*R#define CR '\n\#define CR '\n\#define PROMPT "#define PROMPT " 面积是面积是 ""main()main(){{ cout<<PROMPT<<AREA<<CRcout<<PROMPT<<AREA<<CR;;}}

#include<iostream.h>#include<iostream.h>main()main(){{cout<<"cout<<" 面积是面积是 : "<<3.141592: "<<3.141592 ** 2.82.8** 2.8<<‘2.8<<‘\n';\n';}}

Page 49: 第五章 函数与编译预处理

49

5.8.2 5.8.2 宏定义宏定义二、带参数的宏定义二、带参数的宏定义语法:语法:## definedefine 宏名(参数表) 表达式 宏名(参数表) 表达式 

例:例:#define#define    MAX(a,b) a>b?(a:b MAX(a,b) a>b?(a:b

#define#define MIN(a,b) a<b?a:b MIN(a,b) a<b?a:b

#define #define SQR(x) (x)*(x)SQR(x) (x)*(x)

#define #define AREA(r) 3.1241592*(r)*(r)AREA(r) 3.1241592*(r)*(r)

#define #define mult(x,y) (x)*(y)mult(x,y) (x)*(y)

Page 50: 第五章 函数与编译预处理

50

定义:函数直接或间接的调用自身叫函数的定义:函数直接或间接的调用自身叫函数的递归调用递归调用

说明说明C++C++ 编译系统对递归函数的自调用次数没有限制编译系统对递归函数的自调用次数没有限制每调用函数一次,在内存堆栈区分配空间,用于存放函每调用函数一次,在内存堆栈区分配空间,用于存放函

数变量、返回值等信息,所以递归次数过多,可能引起数变量、返回值等信息,所以递归次数过多,可能引起堆栈溢出堆栈溢出

int f(int x){ int y,z; …… z=f(y); ……. return(2*z);}

int f1(int x){ int y,z; …… z=f2(y); ……. return(2*z);}

int f2(int t){ int a,c; …… c=f1(a); ……. return(3+c);}

5.9 5.9 递归调用递归调用

Page 51: 第五章 函数与编译预处理

51

例 求 n 的阶乘 ( 取 n=5)

)1()!1(

)1,0(1!

nnn

nn

#include <iostream.h>long fac(int n){ int f;  if(n==0||n==1)     f=1; else     f=n*fac(n-1); return (f);}main(){ int n, long y; cout<<"Input a integer number:"; cin>>n; y=factorial(n); cout<<"n="<<n<<" y="<<y;}

5.9 5.9 递归调用递归调用

fac(5)=5*fac(4)

fac(4)=4*fac(3)

fac(3)=3*fac(2)

fac(2)=2*fac(1)

fac(1)=1 11

2*1=2*1=22

3*2=3*2=66

4*6=4*6=2424

5*24=5*24=120120

Page 52: 第五章 函数与编译预处理

52

例 Hanoi 问题 ( 以 3 个盘子为例 )

B CA

5.9 5.9 递归调用递归调用 AA 、、 BB 、、 CC三个柱子,三个柱子, AA柱上有柱上有 nn 个个大小不一的盘子,盘子由大到小从下到大小不一的盘子,盘子由大到小从下到上放置,要求将上放置,要求将 AA柱上的盘子移到柱上的盘子移到 CC柱柱上,要求如下:上,要求如下:

一次只能移动一只盘子;一次只能移动一只盘子;可以借助三根柱子,但任何时候都可以借助三根柱子,但任何时候都

不允许大盘子在小盘子上面。不允许大盘子在小盘子上面。

Page 53: 第五章 函数与编译预处理

53

B CA

5.9 5.9 递归调用递归调用

Page 54: 第五章 函数与编译预处理

54

#include<iostream.h>#include<iostream.h>void move(char x, char y)void move(char x, char y){ static long cnt=0;{ static long cnt=0; cnt++;cnt++; cout<<cnt<<":"<<x<<"cout<<cnt<<":"<<x<<" ->-> "<<y<<endl;"<<y<<endl; }}////////////////////////////////////////////////////////////void hanoi(int n,char A,char B,char C)void hanoi(int n,char A,char B,char C){ if(n==1) { if(n==1) move(A,C);move(A,C); elseelse {{hanoi(n-1,A,C,B);hanoi(n-1,A,C,B);      move(A,C);move(A,C);    hanoi(n-1,B,A,C);hanoi(n-1,B,A,C); }}}}

void main()void main(){ int n;{ int n; cout<<"input plates number:";cout<<"input plates number:"; cin>>n; cin>>n; cout<<‘\n';cout<<‘\n'; hanoi(n,'A','B','C');hanoi(n,'A','B','C'); cin>>n;cin>>n;}}

Page 55: 第五章 函数与编译预处理

55

例:设计一个程序,输入一个整数,利用递例:设计一个程序,输入一个整数,利用递归算法实现爱位正序和反序输出归算法实现爱位正序和反序输出

Page 56: 第五章 函数与编译预处理

56

////逆序输出整数逆序输出整数 nnvoid reverse_order(int n)void reverse_order(int n){{ if(n==0)if(n==0)

return ;return ; elseelse

{{ cout<<n%10;cout<<n%10; reverse_order(n/10);reverse_order(n/10);}}

}}

void main()void main(){{

norm_order(3456);norm_order(3456);cout<<endl;cout<<endl;

reverse_order(3456);reverse_order(3456);cout<<endl;cout<<endl;

}}

////正序输出整数正序输出整数 nnvoid norm_order(int n)void norm_order(int n){{ if(n==0)if(n==0)

return ;return ; elseelse

{{ norm_order(n/10);norm_order(n/10); cout<<(n%10);cout<<(n%10);}}

}}