460 likes | 567 Views
第十次课 2 学时. 第5章 函数. 2. C 程序的循环结构. 学习重点 1. 全局变量的应用 2. 静态型变量的作用域和生存期 3. 宏定义及其调用. 学习目的 1. 掌握局部变量、全局变量的概念和特点 2. 掌握变量的存储类别及变量的作用域和生存期 3. 宏定义及其调用 4. 文件包含的处理. 学习难点 1. 全局变量的应用 2. 静态型变量的作用域和生存期. C 程序的循环结构. 全局变量和局部变量. 变量的存储类别. 内部函数和外部函数. 编译预处理. 5.8 全局变量与局部变量.
E N D
第十次课 2学时 第5章 函数
2 C程序的循环结构 学习重点 1.全局变量的应用 2.静态型变量的作用域和生存期 3.宏定义及其调用 学习目的 1.掌握局部变量、全局变量的概念和特点 2.掌握变量的存储类别及变量的作用域和生存期 3.宏定义及其调用 4.文件包含的处理 学习难点 1.全局变量的应用 2.静态型变量的作用域和生存期
C程序的循环结构 全局变量和局部变量 变量的存储类别 内部函数和外部函数 编译预处理
5.8 全局变量与局部变量 所有的变量都有自己的作用域。变量说明的位置不同,其作用域也不同,据此将C语言中的变量分为内部变量和外部变量。 5.8.1 局部变量 在一个函数内部定义的变量是内部变量,它只在该函数范围内有效。所以内部变量也称“局部变量”。 例如: { int b,c; ...... a,b,c的作用域 }
int f2(int x) /* 函数f2 */ { int y,z; ...... x,y,z的作用域 } main() /* 主函数 */ { int m,n; ...... m,n的作用域 } ①主函数main()中定义的内部变量,也只能在主函数中使用,其它函数不能使用。同时,主函数中也不能使用其它函数中定义的内部变量。
②形参变量也是内部变量,属于被调用函数;实参变量,则是调用函数的内部变量。②形参变量也是内部变量,属于被调用函数;实参变量,则是调用函数的内部变量。 ③ 允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。 ④ 在复合语句中也可定义变量,其作用域只在复合语句范围内。 5.8.2 全局变量 在函数外部定义的变量称为外部变量。以此类推,在函数外部定义的数组就称为外部数组。外部变量不属于任何一个函数,其作用域是:从外部变量的定义位置开始,到本文件结束为止。 外部变量可被作用域内的所有函数直接引用,所以外部变量又称全局变量。
如 void fun1( ); /* 函数声明 */ void fun2( ); int sum=0; /* 定义全局变量sum */ void main( ) /* 主函数 */ {int m, n; ...... sum++; ......} void fun1( ) /* 定义函数fun1 */ {int a; ...... sum--; ......}
int test; /* 定义全局变量test */ void fun2( ) /* 定义函数fun2 */ {int b; ...... sum=test+b; ...... } ① 全局变量的使用,相当于为函数之间的数据传递另外开辟了一条通道。全局变量的生存期是整个程序的运行期间,因此可以利用全局变量从函数得到一个以上的返回值。 【例5-14】输入长方体的长、宽、高,求长方体体积及正、侧、顶三个面的面积。
#include "stdio.h" float s1,s2,s3; float vs(float a,float b,float c) {float v; v=a*b*c; s1=a*b; s2=b*c; s3=a*c; return v; } void main() {float v,l,w,h; printf("input length,width and height:\n"); scanf("%f %f %f",&l,&w,&h); v=vs(l,w,h); printf("v=%6.2f,s1=%6.2f,s2=%6.2f,s3=%6.2f \n",v,s1,s2,s3); }
②全局变量虽然可加强函数模块之间的数据联系,但又使这些函数依赖这些全局变量,因而使得这些函数的独立性降低。②全局变量虽然可加强函数模块之间的数据联系,但又使这些函数依赖这些全局变量,因而使得这些函数的独立性降低。 ③ 在同一源文件中,允许全局变量和局部变量同名。在局部变量的作用域内,全局变量将被屏蔽而不起作用。 【例5-15】全局变量和局部变量同名。 #include "stdio.h" int m=13; int fun(int x,int y) {int m=3; return (x*y-m); }
void main( ) {int a=7,b=5; printf("%d\n",fun(a,b)/m);} 5.9 变量的存储类别 5.9.1 静态存储方式与动态存储方式 从变量值存在的时间(即生存周期)角度来分,可以分为静态存储方式和动态存储方式。 所谓静态存储方式是指在程序运行期间分配固定的存储空间的方式。而动态存储方式则是在程序运行期间根据需要进行动态的分配存储空间的方式。
一个C程序在内存中可供使用的存储空间分为三部分,动态存储区用来存放函数调用时的现场保护和返回地址、自动类别的局部变量和函数形参等数据。以上数据,在函数调用开始时分配动态存储空间,函数调用结束时释放这些空间。静态存储区用以存放全局变量及静态类别的局部变量。在程序执行过程中,它们占据固定的存储单元,而不是动态地进行分配和释放。 在C语言中,对变量的存储类型说明有以下四种:自动变量(auto)、寄存器变量(register)、外部变量(extern)、静态变量(static)。自动变量和寄存器变量属于动态存储方式,外部变量和静态内部变量属于静态存储方式。
5.9.2 自动型变量auto 定义格式:[auto] 数据类型变量表; 函数中的局部变量,如不专门声明为static存储类别,则都是动态分配存储空间,都为自动变量。 auto变量的存储特点: ①自动变量属于动态存储方式。在函数中定义的自动变量,只在该函数内有效;函数被调用时分配存储空间,调用结束就释放。 ②在复合语句中定义的自动变量,只在该复合语句中有效;退出复合语句后,也不能再使用,否则将引起错误。 ③ 定义而不初始化,则其值是不确定的。如果初始化,则赋初值操作是在调用时进行的,且每次调用都要重新赋一次初值。
④由于自动变量的作用域和生存期,都局限于定义它的个体内(函数或复合语句),因此不同的个体中允许使用同名的变量而不会混淆。即使在函数内定义的自动变量,也可与该函数内部的复合语句中定义的自动变量同名。④由于自动变量的作用域和生存期,都局限于定义它的个体内(函数或复合语句),因此不同的个体中允许使用同名的变量而不会混淆。即使在函数内定义的自动变量,也可与该函数内部的复合语句中定义的自动变量同名。 5.9.3 静态型变量static 定义格式: static 数据类型 局部变量表; 存储特点: ① 静态局部变量属于静态存储。在程序执行过程中,即使所在函数调用结束也不释放。换句话说,在程序执行期间,静态局部变量始终存在,但其它函数是不能引用它们的。 ② 定义但不初始化,则自动赋以"0"(整型和实型)或'\0'(字符型)。
③静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时已有初值。以后每次调用它们所在的函数时,不再重新赋初值,只是保留上次调用结束时的值!③静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时已有初值。以后每次调用它们所在的函数时,不再重新赋初值,只是保留上次调用结束时的值! 使用静态局部变量的场合: ① 需要保留函数上一次调用结束时的值。 ②如果初始化后,变量只被引用而不改变其值,则这时用静态局部变量比较方便,以免每次调用时重新赋值。 【例5-16】静态局部变量的存储特性。 #include "stdio.h" void auto_static() {int var_auto=0 ;/* 自动变量:每次调用都重新初始化 */ static int var_static=0 ;/* 静态局部变量:只初始化1次 */
printf("var_auto=%d, var_static=%d\n", var_auto, var_static) ; ++var_auto ; ++var_static ; } void main( ) {int i ; for(i=0 ; i<3 ; i++) auto_static() ; }
【例5-17】打印1到4的阶乘值。 #include "stdio.h" int fac(int n) {static int f=1; f=f*n; return(f); } main( ) {int k; for(k=1;k<=4;k++) printf("%d!=%d \n",i,fac(k)); }
程序的运行结果如下: 1! =1 2! =2 3! =6 4! =24 5.9.4 寄存器型变量register 一般情况下,变量的值都是存储在内存中的。为提高执行效率,C语言允许将局部变量的值存放到寄存器中,这种变量就称为寄存器变量。定义格式如下: register 数据类型变量表;
5.9.5 外部参照型变量extern 外部变量的作用域是从定义点到本文件结束。在此作用域内,全局变量可以为程序中各个函数所引用。编译时将外部变量分配在静态存储区。 有时需要用extern来声明外部变量,以扩展其作用域。 外部变量说明的一般形式为: extern 数据类型外部变量名表; 1.在同一文件内用extern来扩展全局变量的作用域 【例5-19】用extern声明外部变量,扩展程序文件中的作用域。 #include "stdio.h" int vs(int xl,int xw)
{extern int xh; /* 外部变量xh的声明 */ int v; v=xl*xw*xh; /* 直接使用外部变量xh的值 */ return v;} void main( ) {extern int xw,xh; /* 外部变量的声明 */ int xl=5; /* 内部变量的定义 */ printf("xl=%d,xw=%d,xh=%d,v=%d\n",xl,xw,xh,vs(xl,xw)); }
int xl=3,xw=4,xh=5; /* 外部变量xl、xw、xh的定义 */ 程序的运行结果如下: xl=5,xw=4,xh=5,v=100 2.在多个文件中用extern来扩展全局变量的作用域 当一个程序由多个编译单位组成,并且在每个文件中均需要引用同一个全局变量,这时若在每个文件中均定义了一个所需的同名全局变量,在单独编译每个文件时并无异常,编译程序将按定义分别为它们开辟存储空间;而当进行“连接”时,将会产生“重复定义”错误。解决的办法通常是:在其中一个文件中定义所有全局变量,而在其它用到这些全局变量的文件中用extern对这些变量进行说明,声明这些变量已在其它编译单位中定义,通知编译程序不必再为它们开辟存储单元。
5.9.6 用static声明外部变量 当用static说明符说明外部变量时,此变量可称作“静态”全局变量。静态全局变量只允许被本源文件中的函数引用,不允许被其它源文件中的函数引用。 需要说明的是,对外部变量加static声明,并不意味着这时才是静态存储(存放在静态区域中),而不加static的是动态存储(存放在动态区域中)。两种形式的外部变量都是静态存储方式,只是作用范围不同而已,都是在编译时分配内存的。
5.10 外部函数与内部函数 所有函数在本质上都是外部的,因为一个函数要被另外的函数调用,但是,也可以指定函数不能被其他文件调用。当一个源程序由多个源文件组成时,C语言根据函数能否被其它源文件中的函数调用,将函数分为内部函数和外部函数。 5.10.1 内部函数(又称静态函数) 如果在一个源文件中定义的函数,只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用,这种函数称为内部函数。在定义一个内部函数时,只需在函数类型前再加一个“static”关键字即可,如下所示:
static 函数类型 函数名(函数参数表) 如:static int fun(int x,int y) 5.10.2 外部函数 外部函数的定义:在定义函数时,如果没有加关键字“static”,或冠以关键字“extern”,表示此函数是外部函数: [extern] 函数类型 函数名(函数参数表) 如:extern int fun(int x,int y) 或int fun(int x,int y)
调用外部函数时,需要对其进行声明: extern 函数类型函数名(参数类型表)[,函数名2(参数类型表2)……]; 5.11 编译预处理 所谓预处理就是在C编译系统对源程序进行编译之前,先对程序中以符号“#”开头的一些特殊的命令进行“预处理”,即根据预处理命令对程序做相应的处理,然后再由编译程序对预处理后的源程序进行通常的编译处理,得到可供执行的目标代码。 编译预处理功能主要有宏定义、文件包含和条件编译三种。它们分别用“宏定义”命令、“文件包含”命令和“条件编译”命令来实现。预处理命令以符号“#”开头,语句末尾不加分号“;”。
5.11.1 宏定义 宏定义又称为宏代换,分为不带参数的宏定义和带参数的宏定义两种类型。 1.不带参数的宏定义 不带参数的宏定义是指用一个指定的标识符(即宏名)来代表一个字符串(即所代表的内容),其定义格式为: #define 宏名 宏体 …… #undef 宏名
其中#define是宏定义命令,宏名为标识符,宏体为一字符串。宏定义实际上相当于定义符号常量,它的作用是用宏名完全代替宏体。#undef命令控制宏定义的作用域,即宏定义的作用域终止于#undef命令,该命令可省略。例如:#define PI 3.1415926main( ){……}#undef PIfun( ){……}
【例5-21】采用宏定义来定义公式的方法完成求圆的周长、面积和体积。【例5-21】采用宏定义来定义公式的方法完成求圆的周长、面积和体积。 #include "stdio.h" #define R 4.0 #define PI 3.1415 #define L 2*PI*R /* 宏定义中引用已定义的宏名R */ #define S PI*R*R #define V 4.0/3*PI*R*R*R void main( ) {printf("L=%f,\nS=%f,\nV=%f,\n",L,S,V);}
2.带参数的宏定义 宏定义时,在宏名后加上形式参数,就形成了带参数的宏定义。带参数的宏定义,不仅要进行字符替换,还要进行参数替换。其定义格式为: #define宏名(形式参数表)宏体 【例5-22】 使用带参数的宏定义完成例5-2l。 #include "stdio.h" #define PI 3.1415926 #define L(R) 2*PI*R /* R为宏定义中的形参 */ #define S(R) PI*R*R #define V(R) 4.0/3*PI*R*R*R
void main( ) { float r ,l ,a ,v; r=4.0; l=L(r); a=S(r); v=V(r); /* r为实参 ,用来替换形参R */ printf("r=%f\nl=%f\na=%f\nv=%f\n" ,r ,l ,a ,v);} 5.11.2 文件包含 所谓“文件包含”预处理,是指在一个源文件中将另外一个或多个源文件的全部内容包含进来的处理过程,即将另外的文件包含到本文件中。
所谓“文件包含”预处理,是指在一个源文件中将另外一个或多个源文件的全部内容包含进来的处理过程,即将另外的文件包含到本文件中。所谓“文件包含”预处理,是指在一个源文件中将另外一个或多个源文件的全部内容包含进来的处理过程,即将另外的文件包含到本文件中。 #include "文件名" 或 #include<文件名> 有关说明: ① 在文件头部的被包含的文件称为“头文件”或“标题文件”,常以“.h”为后缀(h为head的缩写),如“format.h”等文件。 ② 一个#include命令只能指定一个被包含文件,如果要包含n个文件,必须要用n个#include命令。
③如果文件l包含文件2,而文件2中要用到文件3的内容,则可在文件l中用两个#include命令分别包含文件2和文件3,且文件3应出现在文件2之前,即在文件l中定义。③如果文件l包含文件2,而文件2中要用到文件3的内容,则可在文件l中用两个#include命令分别包含文件2和文件3,且文件3应出现在文件2之前,即在文件l中定义。 ④文件包含可以嵌套,即在一个被包含文件中又可以包含另一个被包含文件 5.11.3 条件编译 所谓“条件编译”,就是对C源程序中某一部分内容指定编译或不编译条件,当满足相应条件时才对该部分内容进行编译或不编译。 常用的条件编译命令有以下三种格式:
格式一: #ifdef宏名 程序段l #eIse 程序段2 #endif 或 #ifdef宏名 程序段l #endif
该命令的作用是:如果#ifdef后的宏名在此之前已经被#define命令定义过,则在程序编译阶段只编译程序段l,否则编译程序段2;如果没有#else部分,当宏名在此之前末被#define命令定义过,编译时直接跳过#endif,否则编译程序段l。这里的“程序段”可以是语句组,也可以是命令行。该命令的作用是:如果#ifdef后的宏名在此之前已经被#define命令定义过,则在程序编译阶段只编译程序段l,否则编译程序段2;如果没有#else部分,当宏名在此之前末被#define命令定义过,编译时直接跳过#endif,否则编译程序段l。这里的“程序段”可以是语句组,也可以是命令行。 【例5-23】若在同一个目录下有文件f11e1.c和file2.h,指出下面程序的输出结果。 file2.h的内容如下: #define DE filel.c的内容如下:
#include "stdio.h" #include "file2.h" /* 文件filel.c包含文件file2.h的宏定义,运行时候需要加上路径 */ #ifdef DE #define R 1.0 /* 程序段1 */ #else #define R 2.0 /* 程序段2 */ #endif
void main( ) { float s; s=3.14*R*R; printf("%f\n",s); } 格式二:#ifndef宏名 程序段l #else 程序段2 #endif
或 #ifndef 宏名 程序段l #endif #ifndef命令的功能与#ifdef相反。如果宏名在此之前末被定义,则编译程序段1,否则编译程序段2。
格式三: #if 表达式 程序段l #else 程序段2 #endif 或 #if 表达式 程序段1 #endif
该命令的功能是:首先求表达式的值,若为真(非零),就编译程序段1,否则编译程序段2。如果没有#else部分,则当表达式值为假(零)时,直接跳过井endif。这样可使程序在不同的条件下执行不同的功能。该命令的功能是:首先求表达式的值,若为真(非零),就编译程序段1,否则编译程序段2。如果没有#else部分,则当表达式值为假(零)时,直接跳过井endif。这样可使程序在不同的条件下执行不同的功能。
本次课学习小结 1.全局变量和局部变量及应用 2. 动态型和静态型变量 3.内部函数与外部函数 4. 宏定义
练习: 一、选择题:(2007年9月份考题) (40) 在一个C语言源程序文件中所定义的全局变量,其作用域为:A) 所在文件的全部范围 B) 所在程序的全部范围C) 所在函数的全部范围 D) 由具体定义位置和extern 说明来决定范围
练习: 二、填空题:(2007年9月份考题) (41)有以下程序程序运行结果是__。 #include int a=1;int f(int c){static int a=2;c=c+1;return (a++)+c;}
main(){ int i,k=0;for(i=0;i<2;i++){int a=3;k+=f(a);}k+=a;printf(“%d\n”,k);}
练习: 三、程序修改题: 下面程序输出1到10的阶乘。改正下面程序中的错误。 #include “stdio.h” void main( ) { int fac(int n); int i; for(i=1;i<=10;i++ printf(“%d!=%d\n”,i,fac(i)); } int fac(int n)
{ int f=1; f=f*n; return(f); } 错误语句: 正确语句:
练习: 四、课后练习题 编程题: 请写出一个宏定义MYALPHA(c),用以判断c是否是字母。若是得1,否则得0