《函数的定义和调用课件》由会员分享,可在线阅读,更多相关《函数的定义和调用课件(38页珍藏版)》请在金锄头文库上搜索。
1、C+程序设计程序设计第第3章章(1) 函数的定义和调用函数的定义和调用1函数的定义和调用主要内容主要内容l函数概述函数概述l函数的定义函数的定义l函数的调用和返回函数的调用和返回l函数的参数传递函数的参数传递值传递值传递l函数的参数传递函数的参数传递引用传递引用传递l函数的原型声明函数的原型声明l全局变量及其作用域全局变量及其作用域l局部变量及其作用域局部变量及其作用域lC+程序的内存布局程序的内存布局l标识符的作用域标识符的作用域块作用域、文件作用域、函数原型作用域、函数作用域块作用域、文件作用域、函数原型作用域、函数作用域l变量的存储类型和生存期变量的存储类型和生存期l函数的调用机制函数的
2、调用机制2函数概述函数概述l关于关于C+函数:函数: C+程序的结构化特点:程序的结构化特点:就是整个程序由一个或多个函数组成,每个函数具有相对独就是整个程序由一个或多个函数组成,每个函数具有相对独立的功能,函数之间有明显的界面,程序整体具有清晰的模块结构,易于修改。立的功能,函数之间有明显的界面,程序整体具有清晰的模块结构,易于修改。 C+函数有两大类:函数有两大类:一类是系统预定义的,称为库函数或标准函数,这些库函数按照一类是系统预定义的,称为库函数或标准函数,这些库函数按照不同的功能进行分类,集中定义在不同的头文件中,用户只要在自己的程序中包含不同的功能进行分类,集中定义在不同的头文件中
3、,用户只要在自己的程序中包含某个头文件,就可直接使用该头文件中定义的函数。另一类是用户自定义函数,用某个头文件,就可直接使用该头文件中定义的函数。另一类是用户自定义函数,用户可以根据需要将程序中某个具有相对独立功能的程序段定义为函数。户可以根据需要将程序中某个具有相对独立功能的程序段定义为函数。 C+程序的基本模块:程序的基本模块:就是函数,任何一个就是函数,任何一个C+程序均由若干个函数组成,其中有且程序均由若干个函数组成,其中有且仅有一个主函数,它是程序执行的入口函数,仅有一个主函数,它是程序执行的入口函数, VC+控制台编程是由用户定义的控制台编程是由用户定义的main()作为入口函数,
4、作为入口函数,Windows编程是由编译器定义的编程是由编译器定义的WinMain()作为入口函数。作为入口函数。3函数概述函数概述 C+函数之间是并列平等的关系,在程序中的定义位置任意,一个函数可以调用其它函数之间是并列平等的关系,在程序中的定义位置任意,一个函数可以调用其它函数,也可以被其他函数所调用,但主函数只能调用其他函数,而不能被调用。函数,也可以被其他函数所调用,但主函数只能调用其他函数,而不能被调用。 函数定义的并列关系函数定义的并列关系 函数调用的层次关系函数调用的层次关系void f1( ) f1的函数体的函数体 void f2( ) f2的函数体的函数体 void main
5、( ) main的函数体的函数体 void f3( ) f3的函数体的函数体 void f4( ) f4的函数体的函数体 void f5( ) f5的函数体的函数体 main( )f1( )f2( )f3( )f4( )f6( )4函数的定义函数的定义l函数的定义函数的定义 : 格式:格式: 返回值类型返回值类型 函数名函数名 ( 类型类型1 形参形参1 ,类型类型2 形参形参2 , ,类型类型n 形参形参n ) 语句序列语句序列 说明:说明: 返回值类型返回值类型可以是可以是C+中任意基本数据类型、已定义的构造数据类型;中任意基本数据类型、已定义的构造数据类型; 若函数没有返回值,则返回值类
6、型定义为若函数没有返回值,则返回值类型定义为 void 型;型; 若函数返回值类型省略,表示返回值类型为若函数返回值类型省略,表示返回值类型为 int 型。型。函数体函数体函数头函数头5函数的定义函数的定义 函数名函数名按标识符命名规则命名,其后括号内定义的是函数的形式参数;按标识符命名规则命名,其后括号内定义的是函数的形式参数; 若该函数为无参函数,括号内的若该函数为无参函数,括号内的 void 通常省略不写,但括号不能省略!通常省略不写,但括号不能省略! 形式参数表形式参数表列出所有形式参数的数据类型、参数名称;列出所有形式参数的数据类型、参数名称; 各参数即使类型相同,也必须分别进行类型
7、说明;各参数即使类型相同,也必须分别进行类型说明; 形参类型可以是形参类型可以是C+中任意基本数据类型、已定义的构造数据类型。中任意基本数据类型、已定义的构造数据类型。l函数参数的作用:函数参数的作用:函数调用时,主调函数通过参数将数据传递给被调函数。函数调用时,主调函数通过参数将数据传递给被调函数。l函数返回值的作用:函数返回值的作用:函数返回时,被调函数可将一个确定的值带回到主调函数。函数返回时,被调函数可将一个确定的值带回到主调函数。 若函数有返回值,则若函数有返回值,则 return语句格式为:语句格式为: return 表达式表达式 ; 且且 return 后面表达式值的类型必须与函
8、数返回值类型相匹配!后面表达式值的类型必须与函数返回值类型相匹配! 若函数无返回值,则返回值类型定义为若函数无返回值,则返回值类型定义为void,且,且return语句格式为:语句格式为: return ;6函数的调用和返回函数的调用和返回l函数的调用函数的调用 : 格式:格式: 函数名函数名 ( 实参实参1 ,实参实参2 , ,实参实参n ) 调用:调用:就是控制从主调函数转去执行被调函数的函数体。调用时,系统要将实参值按就是控制从主调函数转去执行被调函数的函数体。调用时,系统要将实参值按位置传递给对应的形参,因此,一般情况下实参与形参在个数、排列顺序上都必须位置传递给对应的形参,因此,一般
9、情况下实参与形参在个数、排列顺序上都必须一一对应,且类型匹配。一一对应,且类型匹配。l函数的返回函数的返回 :就是控制从被调函数返回到主调函数的调用点。返回时,即在被调函就是控制从被调函数返回到主调函数的调用点。返回时,即在被调函数中执行到数中执行到 return语句时,若有返回值带回,则先计算语句时,若有返回值带回,则先计算 return语句后面表达式的值,语句后面表达式的值,并转换成所规定的返回值类型,此时系统会在内存中建立一个临时变量,用来存放并转换成所规定的返回值类型,此时系统会在内存中建立一个临时变量,用来存放该返回值,待主调函数中包含该调用式的表达式语句从该临时变量中取值后,系统该
10、返回值,待主调函数中包含该调用式的表达式语句从该临时变量中取值后,系统便撤消该临时变量。便撤消该临时变量。7【例】(定义求三个实数中最大数的函数【例】(定义求三个实数中最大数的函数max()。)。)# include float max ( float x , float y , float z ) /要将三个实数传递给要将三个实数传递给max()函数,需定义三个形参。函数,需定义三个形参。 float t = yz ? y : z ; return ( xt ? x : t ) ;void main ( ) float a , b , c ; cout a b c ; cout “最大数:最
11、大数:” max( a , b , c ) endl ; 运行:运行:请输入三个实数:请输入三个实数:13 56 -89 最大数:最大数:56main()函数函数调用调用max( 13, 56, -89 )main()函数的后续语句函数的后续语句函数函数max( 13, 56, -89 )return 56主调函数主调函数被调函数被调函数8函数的参数传递函数的参数传递l形参与实参:形参与实参: 函数定义时,写在函数头部参数表中的变量,称为形式参数(形参)。函数定义时,写在函数头部参数表中的变量,称为形式参数(形参)。 函数调用时,写在调用式参数表中的表达式、变量、常量,称为实在参数(实参)函数
12、调用时,写在调用式参数表中的表达式、变量、常量,称为实在参数(实参)l形参与实参的结合:形参与实参的结合:编译时,并不为各函数的形参分配存储空间,只有在该函数被编译时,并不为各函数的形参分配存储空间,只有在该函数被调用时,其形参才占用存储空间,并通过堆栈从主调函数中获得值,这个过程称为调用时,其形参才占用存储空间,并通过堆栈从主调函数中获得值,这个过程称为形参与实参的结合,一旦函数调用结束,该函数的形参就被撤销。形参与实参的结合,一旦函数调用结束,该函数的形参就被撤销。l函数的参数传递方式有三种函数的参数传递方式有三种 : 值传递值传递 引用传递引用传递 地址传递地址传递9函数的参数传递函数的
13、参数传递值传递值传递l值传递:值传递: 调用函数进行调用函数进行值传递值传递时,调用式中对应的时,调用式中对应的实参可以是常量、变量、表达式实参可以是常量、变量、表达式。 先计算出各个实参表达式的值,并将值赋给对应的形参变量,其过程就是赋值,因此,先计算出各个实参表达式的值,并将值赋给对应的形参变量,其过程就是赋值,因此,要求实参值的类型与形参变量类型要求实参值的类型与形参变量类型符合赋值兼容符合赋值兼容。 由于值传递就是赋值传递,因此是由于值传递就是赋值传递,因此是单向传递单向传递,即实参值传递给形参变量后,若形参变,即实参值传递给形参变量后,若形参变量的值发生变化不会影响到对应的实参变量。
14、量的值发生变化不会影响到对应的实参变量。【例】【例】 # include void swap ( int a , int b ) int t = a ; a = b ; b = t ; void main ( ) int x , y ; cout x y ; cout “调用前:调用前:x = ” x “ty = ” y endl ; swap( x , y ) ; cout “调用后:调用后:x = ” x “ty = ” y endl ; 运行:运行:请输入两个整数:请输入两个整数: 58 97 调用前:调用前:x=56 y=97调用后:调用后:x=56 y=9710【例】【例】# inc
15、lude int maxi ( int a , int b )return ab ? a : b ; float maxf ( float a , float b )return ab ? a : b ; void main ( void ) float x = 3.4 , y = 5.6 ; char c1=A , c2 = B ; int i = 20 , j = 30 ; cout maxi ( x , y ) t ; cout maxf ( x , y ) t ; cout maxi ( c1 , c2 ) t ; cout maxf ( c1 , c2 ) t ; cout maxi
16、 ( i+j , 45+y ) t ; cout maxf ( i+j , 45+y ) endl ;运行:运行:5 5.6 66 66 50 55.6float x=3.4 int a=3float y=5.6 int b=5float x=3.4 float a=3.4float y=5.6 float b=5.6char c1=A int a=65char c2=B int b=66char c1=A float a=65char c2=B float b=66表达式表达式 i+j 值值50,int型型 int a=50表达式表达式 45+y 值值50.6,float型型 int b=5
17、0表达式表达式 i+j 值值50,int型型 float a=50表达式表达式 45+y 值值50.6,float型型 float b=50.611函数的参数传递函数的参数传递引用传递引用传递l引用类型:引用类型:是是C+中一种特殊的数据类型,中一种特殊的数据类型, 定义引用类型变量,其本质是给一个已定义引用类型变量,其本质是给一个已定义的变量起一个别名,系统并不为引用类型变量分配内存空间,引用类型变量与定义的变量起一个别名,系统并不为引用类型变量分配内存空间,引用类型变量与其相关联的变量使用的是同一个内存空间。其相关联的变量使用的是同一个内存空间。l引用类型变量的定义:引用类型变量的定义:
18、格式:格式: & = 功能:功能:定义一个引用类型变量,必须同时对其进行初始化,使之与一个已定义过的同定义一个引用类型变量,必须同时对其进行初始化,使之与一个已定义过的同类型变量相关联,即给该已定义过的变量起一个别名。类型变量相关联,即给该已定义过的变量起一个别名。【例】【例】 # include void main ( ) int x = 5 ; int & x1 = x ; int x2 = x ; x1 += 2 ; cout “x = ” x “tx1 = ” x1 “tx2 = ” x2 endl ; 57xx15x2运行:运行:x = 7 x1= 7 x2 = 512函数的参数传递
19、函数的参数传递引用传递引用传递l引用类型变量可以作为函数的形参,以实现参数的引用传递。引用类型变量可以作为函数的形参,以实现参数的引用传递。 【例】【例】 void swap ( int &a , int &b ) int t = a ; a = b ; b = t ; l引用传递:引用传递: 调用函数时,系统对引用类型的形参是调用函数时,系统对引用类型的形参是不分配不分配存储空间的,其本质是给对应的实参变存储空间的,其本质是给对应的实参变量起一个别名。量起一个别名。 进行进行引用传递引用传递时,调用式中对应的时,调用式中对应的实参只能是变量实参只能是变量。 引用传递时传递的是变量名,引用类型
20、的形参变量与其相关联的实参变量使用的是同引用传递时传递的是变量名,引用类型的形参变量与其相关联的实参变量使用的是同一个存储空间,若引用类型的形参变量的值发生变化,其相关联的实参变量的值也一个存储空间,若引用类型的形参变量的值发生变化,其相关联的实参变量的值也就随之变化,因此是就随之变化,因此是双向传递双向传递。13【例】(值传递)【例】(值传递) # include void swap ( int a , int b ) int t = a ; a = b ; b = t ; void main ( ) int x = 5 , y = 10 ; cout “调用前:调用前:x = ” x “t
21、y = ” y endl ; swap( x , y ) ; cout “调用后:调用后:x = ” x “ty = ” y endl ; 【例】(引用传递)【例】(引用传递) # include void swap ( int &a , int &b ) int t = a ; a = b ; b = t ; void main ( ) int x = 5 , y = 10 ; cout “调用前:调用前:x = ” x “ty = ” y endl ; swap( x , y ) ; cout “调用后:调用后:x = ” x “ty = ” y endl ; 运行:运行:调用前:调用前:
22、x=5 y=10调用后:调用后:x=10 y=5引用传递:引用传递:main(): swap(): x a 10 y b 5510运行:运行:调用前:调用前:x=5 y=10调用后:调用后:x=5 y=10值传递:值传递:main(): swap():x a 10y b 551051014函数的原型声明函数的原型声明l函数的原型声明:函数的原型声明: C+中,把函数的定义部分称为函数的定义性声明,而把对函数的引用性声明称为函中,把函数的定义部分称为函数的定义性声明,而把对函数的引用性声明称为函数的原型声明。数的原型声明。 当函数定义在前、调用在后时,可直接调用;当函数调用在前、定义在后时,在函
23、数当函数定义在前、调用在后时,可直接调用;当函数调用在前、定义在后时,在函数被调用前,必须对被调函数作原型声明,函数原型声明可放在主调函数中,也可放被调用前,必须对被调函数作原型声明,函数原型声明可放在主调函数中,也可放在所有函数之外。在所有函数之外。 函数原型声明的目的是告知编译系统,该函数的返回值类型、参数个数、顺序、各参函数原型声明的目的是告知编译系统,该函数的返回值类型、参数个数、顺序、各参数的类型,以便编译系统对其后该函数调用式是否有效进行语法检查。数的类型,以便编译系统对其后该函数调用式是否有效进行语法检查。l函数原型声明的格式:函数原型声明的格式: 格式格式1: 函数头函数头 +
24、 末尾加分号末尾加分号 返回值类型返回值类型 函数名函数名 ( 类型类型1 形参形参1 ,类型类型2 形参形参2 , ,类型类型n 形参形参n ) ; 格式格式2: 函数头(去掉形参变量名)函数头(去掉形参变量名) + 末尾加分号末尾加分号 返回值类型返回值类型 函数名函数名 ( 类型类型1 ,类型类型2 , ,类型类型n ) ;15【例】(输入一个【例】(输入一个8位二进制数,将其转换为十进制数输出。例:若输入位二进制数,将其转换为十进制数输出。例:若输入11010001, 2 = 1(27)+1(26)+0(25)+1(24)+0(23)+0(22)+0(21)+1(20) = 20910
25、 ,输出,输出209。)。)# include void main ( ) double power ( double , int ) ; /power()函数的引用性声明,即原型声明函数的引用性声明,即原型声明 int value = 0 , k ; char c ; cout = 0 ; i- ) cin c ; if ( c = 1 ) value += ( int ) power( 2 , i ) ; /调用调用power()函数函数 cout “十进制数:十进制数:” value endl ; double power ( double x , int n ) /power()函数的
26、定义性声明函数的定义性声明 double t = 1 ; while ( n- ) t *= x ; return t ; 运行:运行:请输入一个请输入一个8位二进制数:位二进制数:11010001 十进制数:十进制数:20916【例】(求【例】(求的值,的值,arctan(x) 用级数计算,直到某项绝对值不大于用级数计算,直到某项绝对值不大于10-15为止。为止。 )# include # include void main ( ) double a , b ; double arctan ( double x ) ; /arctan()函数的引用性声明,即原型声明函数的引用性声明,即原型声
27、明 a = 16 * arctan ( 1 / 5. ) ; /调用调用arctan()函数,且如果写成函数,且如果写成1/5,结果就都是,结果就都是0 b = 4 * arctan ( 1 / 239.0 ) ; /调用调用arctan()函数,且如果写成函数,且如果写成1/239,结果就都是,结果就都是0 cout “= ” ( a-b ) 1e-15 ; i+=2 ) sum += sign * t / i ; t = t * x * x ; sign *= -1 ; return sum ; 运行:运行:= 3.1415917【例】(找出【例】(找出 11999 之间的数之间的数m,使
28、之满足,使之满足m、m2、m3均为回文数。均为回文数。 回文:各位数字左右对称的整数。例:回文:各位数字左右对称的整数。例:11、 112=121、113=1331,则输出,则输出11。)。) 分析:从最低位开始,用除分析:从最低位开始,用除10取余的方法,依次取出该数的各位数字;取余的方法,依次取出该数的各位数字; 按反序重新构成新的数,若新数与原数完全相等,则原数为回文。按反序重新构成新的数,若新数与原数完全相等,则原数为回文。# include void main ( ) bool symm ( int n ) ; /symm()函数的引用性声明,即原型声明函数的引用性声明,即原型声明
29、for ( int m=11 ; m=999 ; m+ ) if ( symm( m ) & symm( m*m ) & symm( m*m*m ) ) cout “m=” m “tm*m=” m*m “tm*m*m=” m*m*m endl ; bool symm ( int n ) /symm()函数的功能是判断整数函数的功能是判断整数n是否是回文是否是回文 for ( int i=n , m=0 ; i != 0 ; i = i/10 ) m = m*10 + i%10 ; / m是新构的数是新构的数 return ( m = n ) ;运行:运行:m=11 m*m=121 m*m*m=
30、1331m=101 m*m=10201 m*m*m=1030301m=111 m*m=12321 m*m*m=136763118【例】(求如下公式,【例】(求如下公式,r 和和s 值键盘输入,值键盘输入,SIN(x)用级数计算,计算精度为用级数计算,计算精度为10-3。)。)# include # include void main ( ) double tsin ( double ) ; double k , r , s ; cout r s ; if ( r*r = s*s ) k = sqrt ( tsin( r ) * tsin( r ) + tsin( s ) * tsin( s )
31、 ) ; else k = tsin( r*s ) / 2 ; cout “r = ” r “ts = ” s “t k = ” k = 1e-3 ; sign *= -1 ) sum += sign * t / p ; t = t * x * x ; i += 2 ; p = p * i * (i-1) ; return sum ; 运行:运行:请输入请输入 r 和和 s 的值:的值:3 1 r = 3 s = 1 k = 0.070437319全局变量及其作用域全局变量及其作用域l关于全局变量:关于全局变量: 作用:作用:若程序中的某个变量要被多个函数所访问,可将其定义为全局变量。若程序中
32、的某个变量要被多个函数所访问,可将其定义为全局变量。 全局变量:全局变量:指指定义在函数之外的变量。定义在函数之外的变量。 作用域:作用域:全局变量可以定义在函数外的任何位置,一旦在某个位置定义了全局变量,全局变量可以定义在函数外的任何位置,一旦在某个位置定义了全局变量,其后的任何函数均可以访问该全局变量。其后的任何函数均可以访问该全局变量。 存储类型:存储类型:全局变量存放在全局数据区(静态区),分配该区时内存自动初始化为全局变量存放在全局数据区(静态区),分配该区时内存自动初始化为0,因此全局变量若不做初始化,其初值为,因此全局变量若不做初始化,其初值为0。 生存期:生存期:全局变量在程序
33、开始运行时就在全局区分配,程序运行结束才被释放。全局变量在程序开始运行时就在全局区分配,程序运行结束才被释放。20局部变量及其作用域局部变量及其作用域l关于局部变量:关于局部变量: 块的概念:块的概念:指程序中用花括号指程序中用花括号“ ”括起来的一个程序段,称为一个块。括起来的一个程序段,称为一个块。 局部变量:局部变量:指定义在函数之内或某个块内的变量。指定义在函数之内或某个块内的变量。 作用域:作用域:局部变量可以定义在块内的任何位置,一旦在块内的某个位置定义了局部变局部变量可以定义在块内的任何位置,一旦在块内的某个位置定义了局部变量,只能在该块内该位置之后的那个区域内才可以访问该局部变
34、量。量,只能在该块内该位置之后的那个区域内才可以访问该局部变量。 存储类型:存储类型:auto自动类型局部变量、自动类型局部变量、static静态类型局部变量。静态类型局部变量。 auto自动局部变量:自动局部变量:存放在局部数据区(栈区),分配栈区时内存不做初始化,因存放在局部数据区(栈区),分配栈区时内存不做初始化,因此此auto局部变量若不做初始化,其初值不确定。局部变量若不做初始化,其初值不确定。 static静态局部变量:静态局部变量:存放在全局数据区(静态区),存放在全局数据区(静态区), 分配该区时内存自动初始化为分配该区时内存自动初始化为0,因此,因此static局部变量若不做
35、初始化,其初值为局部变量若不做初始化,其初值为0。21C+程序的内存布局程序的内存布局l操作系统为一个操作系统为一个C+程序运行定义了四个内存区域:程序运行定义了四个内存区域: 代码区代码区存放程序代码。存放程序代码。 全局数据区(静态区)全局数据区(静态区)存放全局变量、存放全局变量、static静态局部变量。全局变量在程序静态局部变量。全局变量在程序开始运行时就在该区分配;开始运行时就在该区分配; static静态局部变量在程序运行中第一次进入其作用域时静态局部变量在程序运行中第一次进入其作用域时在该区分配。二者都是直到程序运行结束才被释放。在该区分配。二者都是直到程序运行结束才被释放。
36、局部数据区(栈区)局部数据区(栈区) 存放存放auto局部变量。局部变量。 在程序运行到其作用域时在栈区分在程序运行到其作用域时在栈区分配,但怎样分配在编译时就已经确定。配,但怎样分配在编译时就已经确定。auto局部变量在离开其作用域时即被释放。局部变量在离开其作用域时即被释放。 动态数据区(自由存储区、堆区)动态数据区(自由存储区、堆区) 存放运行时由存放运行时由 new运算符动态创建的变量。运算符动态创建的变量。动态创建的变量在编译时无法为它们预定存储空间,系统根据运行时的具体要求在动态创建的变量在编译时无法为它们预定存储空间,系统根据运行时的具体要求在该区进行分配。在该区的变量必须用该区
37、进行分配。在该区的变量必须用 delete运算符才能将其释放。运算符才能将其释放。22标识符的作用域标识符的作用域l作用域:作用域:指程序中标识符(如变量名、函数名)的有效使用范围,即作用范围。指程序中标识符(如变量名、函数名)的有效使用范围,即作用范围。l目的:目的:解决标识符的同名问题。当标识符具有不同的作用域时,允许标识符同名;解决标识符的同名问题。当标识符具有不同的作用域时,允许标识符同名;当标识符的作用域完全相同时,不允许标识符同名。当标识符的作用域完全相同时,不允许标识符同名。lC+中作用域有五种:中作用域有五种: 块作用域块作用域 文件作用域文件作用域 函数原型作用域函数原型作用
38、域 函数作用域函数作用域 类作用域类作用域23标识符的作用域标识符的作用域块作用域块作用域l块:块:指程序中用花括号指程序中用花括号“ ”括起来的一个程序段,称为一个块。括起来的一个程序段,称为一个块。l块作用域:块作用域:在块内声明的变量只能在该块内被引用,开始于变量的声明处,结束于在块内声明的变量只能在该块内被引用,开始于变量的声明处,结束于块的结尾处。块的结尾处。l几条原则:几条原则: 形参变量的作用域:形参变量的作用域:在其所属函数的函数体块内。在其所属函数的函数体块内。 局部变量的作用域:局部变量的作用域:在其所属的块内,该变量的声明位置之后的那个区域内。在其所属的块内,该变量的声明
39、位置之后的那个区域内。 循环语句循环语句 for(表达式(表达式1;表达式;表达式2;表达式;表达式3)中表达式)中表达式1 所声明变量的作用域:所声明变量的作用域: 在在for语句所属的块内,该变量的声明位置之后的那个区域内。语句所属的块内,该变量的声明位置之后的那个区域内。 局部优先原则:局部优先原则:具有块作用域的标识符在其作用域内,将屏蔽其作用块包含本块的同具有块作用域的标识符在其作用域内,将屏蔽其作用块包含本块的同名标识符。名标识符。24【例】(块作用域)【例】(块作用域) # include int fun ( int n ) cout “正在求:正在求:1+2+” n “n” ;
40、 int s = 0 ; for ( int i =1 ; i=n ; i+ ) char a = 7 ; cout a ; s += i ; return s ;void main ( ) int a = 5 , b = 7 ; cout “第第1次调用:次调用:” fun( a ) endl ; int a = 9 , b = 11 ; cout “第第2次调用:次调用:” fun( a ) endl ; cout “第第3次调用:次调用:” fun( b ) endl ; cout “第第4次调用:次调用:” fun( b ) endl ; 形参形参 n 作用域作用域局部局部 s 作用域
41、作用域局部局部 i 作用域作用域局部局部 a 作用域作用域局部局部 a、b 作用域作用域局部局部 a、b 作用域作用域运行:运行:正在求:正在求:1+2+5第第1次调用:次调用:15正在求:正在求:1+2+9第第2次调用:次调用:45正在求:正在求:1+2+11第第3次调用:次调用:66正在求:正在求:1+2+7第第4次调用:次调用:2825【例】(块作用域)【例】(块作用域)# include void main ( ) int a = 10 , b = 20 , c = 30 ; cout “a=” a “tb=” b “tc=” c endl ; int a = 40 , b = 50
42、; c = a + b ; cout “a=” a “tb=” b “tc=” c endl ; cout “a=” a “tb=” b “tc=” c endl ;【例】(块作用域)【例】(块作用域)# include void main ( ) for ( int a = 1 ; a = 5 ; a+ ) int a = 2 ; cout “a = ” +a endl ; cout “a = ” a endl ; 运行:运行:a=10 b=20 c=30 a=40 b=50 c=90 a=10 b=20 c=90 运行:运行:a = 3a = 3a = 3a = 3a = 3a = 626
43、标识符的作用域标识符的作用域文件作用域文件作用域l文件作用域:文件作用域:在函数外声明的变量可在该文件内被引用,开始于变量的声明处,结在函数外声明的变量可在该文件内被引用,开始于变量的声明处,结束于文件的结尾处,即一旦在某个位置定义了全局变量,在其后的整个文件中均可束于文件的结尾处,即一旦在某个位置定义了全局变量,在其后的整个文件中均可以访问。以访问。l几条原则:几条原则: 全局变量作用域从定义处开始到文件结尾处结束,遵循声明在前、使用在后。全局变量作用域从定义处开始到文件结尾处结束,遵循声明在前、使用在后。 当全局变量引用在前、声明在后时,需先对全局变量作外部声明。当全局变量引用在前、声明在
44、后时,需先对全局变量作外部声明。 格式:格式: extern 类型类型 全局变量名全局变量名 ; 当全局变量与块作用域内的局部变量同名时,局部变量优先。当全局变量与块作用域内的局部变量同名时,局部变量优先。 在块作用域内,若要引用同名的全局变量时,可通过域运算符在块作用域内,若要引用同名的全局变量时,可通过域运算符 “ : ” 来引用。来引用。 格式:格式: : 全局变量名全局变量名 27【例】(文件作用域)【例】(文件作用域)# include int a = 5 , b ; /a、b定义在函数外,定义在函数外,为全局变量为全局变量extern int c ; /全局变量全局变量c引用在前、
45、声明在后时,需先外部声明引用在前、声明在后时,需先外部声明void main ( ) int a = 10 , b = 20 ; /a、b定义在函数内,定义在函数内,为局部变量为局部变量 cout “a=” a “tb=” b “tc=” c endl ;a = 18 ; :b = :b + 4 ; /引用全局变量引用全局变量b c = :a +a ; /引用全局变量引用全局变量c和和a,引用,引用局部变量局部变量a cout “a=” a “tb=” b “tc=” c endl ; cout “a=” :a “tb=” :b “tc=” c endl ;int c = 88 ; /c定义在
46、函数外,定义在函数外,为全局变量为全局变量运行:运行:a=10 b=20 c=88 a=18 b=20 c=23 a=5 b=4 c=23 28标识符的作用域标识符的作用域函数原型作用域、函数作用域函数原型作用域、函数作用域l函数原型作用域:函数原型作用域: 在函数原型声明的参数表中声明的标识符,所具有的作用域称为函数原型作用域,即在函数原型声明的参数表中声明的标识符,所具有的作用域称为函数原型作用域,即从其声明处开始,到函数原型声明的结束处结束。从其声明处开始,到函数原型声明的结束处结束。 函数原型声明中所声明的标识符,其作用域仅在函数原型声明的语句内,因此与该函函数原型声明中所声明的标识符
47、,其作用域仅在函数原型声明的语句内,因此与该函数的定义和调用均无关,所以在函数原型声明中可只作参数的类型声明,而省略参数的定义和调用均无关,所以在函数原型声明中可只作参数的类型声明,而省略参数名。数名。 【例】【例】函数原型声明:函数原型声明:void swap ( int a , int b ) ; /a、b作用域仅在该句内作用域仅在该句内 函数原型声明:函数原型声明:void swap ( int , int ) ;l函数作用域:函数作用域: 函数作用域是指在函数内定义的标识符在其定义的函数内均有效,即不论在函数内的函数作用域是指在函数内定义的标识符在其定义的函数内均有效,即不论在函数内的
48、任何地方定义,在整个函数内均可以引用。任何地方定义,在整个函数内均可以引用。 C+中只有语句标号具有函数作用域。中只有语句标号具有函数作用域。29变量的存储类型和生存期变量的存储类型和生存期l存储类型:存储类型:变量的存储类型反映了变量占用内存空间的期限,它规定了变量的生存变量的存储类型反映了变量占用内存空间的期限,它规定了变量的生存期,即何时为变量分配内存空间、何时撤消变量收回为其分配的内存空间。期,即何时为变量分配内存空间、何时撤消变量收回为其分配的内存空间。l在声明变量时,可对其存储类型进行定义,格式:在声明变量时,可对其存储类型进行定义,格式: 存储类型存储类型 类型类型 变量名变量名
49、1 = 初值初值1 ,变量名,变量名2 = 初值初值2 , ; 其中存储类型有四种:其中存储类型有四种: 自动类型自动类型auto 静态静态类型类型static 寄存器类型寄存器类型register 外部类型外部类型extern 当存储类型省略时,对于局部变量则表示当存储类型省略时,对于局部变量则表示auto。30变量的存储类型和生存期变量的存储类型和生存期lauto自动类型变量:自动类型变量: 声明局部变量时,用声明局部变量时,用auto修饰属修饰属自动类型变量,自动类型变量,存放在局部数据区,即栈区存放在局部数据区,即栈区 。 auto局部变量在程序运行到其作用域时在栈区分配,在离开其作用
50、域时即被释放,局部变量在程序运行到其作用域时在栈区分配,在离开其作用域时即被释放,因此生存期仅在其作用域内。因此生存期仅在其作用域内。 声明局部变量时若省略存储类型,声明局部变量时若省略存储类型,C+编译器默认为编译器默认为auto。 自动类型变量未初始化时,其初值不确定。自动类型变量未初始化时,其初值不确定。 全局变量无自动类型。全局变量无自动类型。31变量的存储类型和生存期变量的存储类型和生存期lstatic静态类型变量:静态类型变量: 声明局部变量时,用声明局部变量时,用static修饰属静态修饰属静态类型变量,类型变量,存放在全局数据区,即静态区存放在全局数据区,即静态区 。 stat
51、ic局部变量的生存期:在程序运行中第一次进入其作用域时在静态区分配,离开局部变量的生存期:在程序运行中第一次进入其作用域时在静态区分配,离开其作用域时并不释放,只是不能引用,若程序运行中再次进入该其作用域时并不释放,只是不能引用,若程序运行中再次进入该static局部变量的作局部变量的作用域,由于该变量已经存在,所以不再分配空间,可直接引用。用域,由于该变量已经存在,所以不再分配空间,可直接引用。 static局部变量直到局部变量直到程序运行结束才被释放。程序运行结束才被释放。 static局部变量的局部变量的作用:是保存函数的运行结果,以便再次调用该函数时,能继续使作用:是保存函数的运行结果
52、,以便再次调用该函数时,能继续使用上次调用的计算结果。注意,不在其作用域时,不能直接引用;若需引用,可间用上次调用的计算结果。注意,不在其作用域时,不能直接引用;若需引用,可间接实现,即通过该接实现,即通过该 static 局部变量局部变量的地址进行访问(后续章节介绍)。的地址进行访问(后续章节介绍)。32变量的存储类型和生存期变量的存储类型和生存期 全局变量声明时无论前面加或不加全局变量声明时无论前面加或不加static,都具有静态生存期,因为全局变量都,都具有静态生存期,因为全局变量都存放存放在全局数据区,即静态区在全局数据区,即静态区 。 但用但用 static 修饰的全局变量,称为静态
53、全局变量。静态全局变量与一般的全局变量修饰的全局变量,称为静态全局变量。静态全局变量与一般的全局变量在形式上类似,但含义有所不同,静态全局变量只能在其定义的源文件中有效(该在形式上类似,但含义有所不同,静态全局变量只能在其定义的源文件中有效(该文件中可以有若干个函数),而在该程序的其他源文件中则是无效的;而一般的全文件中可以有若干个函数),而在该程序的其他源文件中则是无效的;而一般的全局变量可以通过局变量可以通过 extern 修饰后,作用于该程序的多个源文件。修饰后,作用于该程序的多个源文件。 静态静态类型变量未初始化时,其初值为类型变量未初始化时,其初值为0。33【例】【例】# inclu
54、de int a = 1 , b = 2 ; /a、b为全局变量,在静态区为全局变量,在静态区int fun ( ) static int a =3 ; /a为静态局部变量,在静态区为静态局部变量,在静态区 int b = 4 ; /b为为auto局部变量,在栈区局部变量,在栈区 a += b ; return a ; void main ( ) int a = 5 , b = 6 ; /a、b为为auto局部变量,在栈区局部变量,在栈区 cout “a=” a “tb=” b endl ; for ( int i=1 ; i=3 ; i+ ) cout “第第” i “次调用:次调用:” f
55、un( ) endl ; cout “a=” a “tb=” b endl ; cout “a=” :a “tb=” :b endl ;运行:运行:a=5 b=6第第1次调用:次调用: 7a=5 b=6第第2次调用:次调用:11a=5 b=6第第3次调用:次调用:15a=5 b=6a=1 b=234【例】(与上例比较)【例】(与上例比较)# include int a = 1 , b = 2 ; /a、b为全局变量,在静态区为全局变量,在静态区int fun ( ) int a = 3 ; /a为为auto局部变量,在栈区局部变量,在栈区 int b = 4 ; /b为为auto局部变量,在栈
56、区局部变量,在栈区 a += b ; return a ; void main ( ) int a = 5 , b = 6 ; /a、b为为auto局部变量,在栈区局部变量,在栈区 cout “a=” a “tb=” b endl ; for ( int i=1 ; i=3 ; i+ ) cout “第第” i “次调用:次调用:” fun( ) endl ; cout “a=” a “tb=” b endl ; cout “a=” :a “tb=” :b endl ;运行:运行:a=5 b=6第第1次调用:次调用: 7a=5 b=6第第2次调用:次调用: 7a=5 b=6第第3次调用:次调用
57、: 7a=5 b=6a=1 b=235变量的存储类型和生存期变量的存储类型和生存期lregister寄存器类型变量:寄存器类型变量: 声明局部变量时,用声明局部变量时,用 register 修饰属寄存器类型变量,含义是向编译系统建议:直修饰属寄存器类型变量,含义是向编译系统建议:直接使用接使用CPU中的寄存器,以提高运算速度,一般用于循环控制变量。中的寄存器,以提高运算速度,一般用于循环控制变量。 全局变量不能用寄存器类型。全局变量不能用寄存器类型。lextern外部类型变量:外部类型变量: 用于修饰全局变量,用于修饰全局变量,C+中有两种情况需要使用中有两种情况需要使用extern外部类型变
58、量。外部类型变量。 情况一:在同一个源程序文件中定义了全局变量,当全局变量引用在前、声明在后时,情况一:在同一个源程序文件中定义了全局变量,当全局变量引用在前、声明在后时,需先对全局变量作外部声明。需先对全局变量作外部声明。 情况二:在由多个文件组成一个完整的程序时,一个源文件中定义的全局变量或函数,情况二:在由多个文件组成一个完整的程序时,一个源文件中定义的全局变量或函数,在另外一个源文件中被引用时,需先对该全局变量或函数作外部声明。在另外一个源文件中被引用时,需先对该全局变量或函数作外部声明。 外部类型变量的声明格式:外部类型变量的声明格式: extern 类型类型 全局变量名全局变量名
59、; 外部类型函数的声明格式:外部类型函数的声明格式: extern 函数的原型声明函数的原型声明 ;36函数的调用机制函数的调用机制l函数调用发生时,系统做的工作:函数调用发生时,系统做的工作: 建立栈空间。建立栈空间。 保护现场,将当前主调函数的执行状态和返回地址保存在栈中。保护现场,将当前主调函数的执行状态和返回地址保存在栈中。 为被调函数的形参变量、局部变量分配栈空间,并将实参值传递给形参。为被调函数的形参变量、局部变量分配栈空间,并将实参值传递给形参。 执行被调函数,直到遇到执行被调函数,直到遇到return语句或函数的结束处。语句或函数的结束处。 释放被调函数的所有局部变量栈空间。释
60、放被调函数的所有局部变量栈空间。 恢复现场,取回主调函数的执行状态和返回地址,释放被调函数的栈空间。恢复现场,取回主调函数的执行状态和返回地址,释放被调函数的栈空间。 返回到主调函数继续执行。返回到主调函数继续执行。37【例】(分析函数调用时的内存变化情况)【例】(分析函数调用时的内存变化情况)# include int fun1 ( int a , int b ) ;int fun2 ( int a ) ;void main ( ) int a = 7 , b = 9 ; /a、b为为auto局部变量局部变量 cout “a=” a “tb=” b endl ; cout “调用:调用:”
61、fun1( a , b ) endl ; cout “a=” a “tb=” b endl ; int fun2 ( int a ) /a为形参变量,在栈区为形参变量,在栈区 int x = 5 ; /x为为auto局部变量,在栈区局部变量,在栈区 return ( a * x ) ; int fun1 ( int a , int b ) /a、b为形参变量,在栈区为形参变量,在栈区 int x = 3 ; /x为为auto局部变量,在栈区局部变量,在栈区 return ( a * b * fun2( x ) ) ; /调用调用fun2()函数函数53fun1()执行状态和返回地址执行状态和返回地址397main() 执行状态和返回地址执行状态和返回地址97操作系统执行状态和返回地址操作系统执行状态和返回地址xaxbaba main() fun1() fun2()栈顶栈顶栈底栈底栈栈 运行:运行:a=7 b=9调用:调用: 945 a=7 b=938