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