c语言程序设计 重庆大学课件 第08章 结构体类型和联合体类型_第1页
c语言程序设计 重庆大学课件 第08章 结构体类型和联合体类型_第2页
c语言程序设计 重庆大学课件 第08章 结构体类型和联合体类型_第3页
c语言程序设计 重庆大学课件 第08章 结构体类型和联合体类型_第4页
c语言程序设计 重庆大学课件 第08章 结构体类型和联合体类型_第5页
已阅读5页,还剩83页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、程序设计技术C语言数据描述和C程序设计初步 结构化程序设计基础和C语言的控制结构 数组及其应用 函数与C程序结构 指针与函数 指针与数组 字符串及其应用 结构体类型和联合体类型 C语言的文件处理及其应用 位运算与枚举类型函数与程序结构函数的定义和调用函数的嵌套调用和递归调用变量的作用域和生存期编译预处理多源文件C程序的组织方法函数与程序结构 模块化程序设计技术就是通过开发和维护一些小的程序块(即模块)的方法构建一个大型程序,是人类解决较大的复杂问题所采用的一种“分而治之”的策略。本章主要讨论C语言实现模块化程序设计技术的手段以及在模块化实现过程中所遇到的一系列问题。4.1 函数的定义和调用 C

2、程序的一般结构 C源程序源文件1源文件i源文件n预处理语句函数1函数n说明/定义部分执行部分图4.1 C程序结构4.1.1 函数的定义和声明 函数定义就是编写完成某种具有一定功能的程序模块。1)现代风格的函数定义形式: 返回类型符 函数名(形式参数表及其说明) /函数头 变量定义和说明及函数执行语句 /函数体 函数体:由变量定义与函数执行语句组成。二者全无则是空函数,先占位置,以后补上。/*现代风格*/long fac(int n) /函数头 long y=1; int i; for(i=1;i=n ;i+)y=y*i; return(y); 4.1.1 函数的定义和声明函数体long为函数返

3、回值类型return语句将n!返回调用函数调用函数处获取n的值函数的定义内容如下:函数返回值类型 可是任何有效类型,void表示函数无返回值。 函数名 用户自定义标识符,不能重名。形式参数(简称形参)的数目、类型 为函数接受外来数据提供变量名、类型和数目。return 语句 函数执行结果(按函数定义的返回类型)返回给主调函数。如果函数定义时返回类型为void,可缺省return语句。4.1.1 函数的定义和声明 注意:C语言中的每一个函数都是一个独立的代码块。一个函数的代码块是隐藏于函数内部的,不能被任何其它函数中的任何语句(除调用它的语句之外)所访问。它既不能影响程序其它部分,也不受其它部分

4、的影响。在一个函数的内部不能定义其他函数(即函数不能嵌套定义)。这个规定保证了每个函数都是一个相对独立的程序模块。由多个函数组成的C程序中,各个函数的定义顺序是任意的,它不影响C程序运行时函数的执行顺序 4.1.1 函数的定义和声明2)函数的声明 在主调函数中,要对本函数将要调用的函数事先进行必要的声明。所谓“声明”是指向编译系统提供必要的信息:函数名,函数的返回值类型,函数参数的个数、类型及排列次序,以便编译系统对函数的调用进行检查。例如,检查形参与实参类型是否一致,函数返回值的类型是否正确。 C语言的函数分为标准库函数和用户自定义函数两大类。下面分别介绍他们的声明方法。 4.1.1 函数的

5、定义和声明标准库函数的声明 使用标准库函数时,由于系统提供的标准库函数的说明都分门别类集中在一些称为“头文件”的文本文件中,所以在程序中如果要调用系统标准库函数,也要在程序的适当位置使用编译预处理语句来进行声明。 例如: #include 或 #inlcude “” 作用:将调用有关库函数的必要信息包含到本源文件中来。4.1.1 函数的定义和声明用户自定义函数的声明 对于用户自定义函数,如果被调用函数(简称被调函数)与调用它的函数(简称主调函数)在同一源文件中,在函数调用之前,需要对被调函数进行声明。 被调函数声明的一般形式:返回类型符 函数名(形式参数表及其说明); 4.1.1 函数的定义和

6、声明#include void main()long factorial(int n);int num;printf(Input the num: );scanf(%d,&num);printf(%d!= %ldn,num,factorial(num);long factorial(int n) /函数factorial的定义inti;long fact=1;for(i=1;i=n;i+)fact*=i; return fact;函数声明告诉编译系统factorial是返回类型long,只有一个int参数的函数程序演示4.1.1 函数的定义和声明在函数声明中,形参变量名字无关紧要 (可与函数定

7、义不同或缺省) factorial的声明语句可写成如下两种形式: long factorial(int); /* 缺省形参名 */ long factorial(int x); /* 形参名与定义不同 */ 注意: 在下列情况下可以不对被调函数进行声明: 被调函数的定义出现在主调函数之前,不必进行说明,其原因是编译系统此时已经知道了被调函数的返回类型。程序演示4.1.1 函数的定义和声明函数调用 一个函数调用另外一个函数以完成某一特定的功能称之为函数调用。调用者称为主调函数被调者称为被调函数函数调用的一般形式:函数名(实参表); 调用时填入的参数,称为实际参数,简称实参。实参的个数、类型和顺序

8、,应该与被调函数的形参个数、类型和顺序一致。4.1.2 值参数传递的函数调用 三种调用函数的方式 函数语句(p119):将函数调用作为一个单独的C语句,此种方式主要对应于返回值为空类型(void)的函数调用。 函数表达式:函数作为一项,出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的。如:c=add(a,b); 函数实参:函数调用作为另外一个函数调用的实际参数出现。此时要求函数被调用后必须要返回一个确定值以作为其外层函数调用的实际参数。 如:if (prime(reverse(n)4.1.2 值参数传递的函数调用 C程序由若干个相对独立的函数组成。在程序运行过程中,当

9、被调函数是有参函数时,函数调用必然伴随着参数传递。当一个函数调用另一个函数时,实参的值传递到形参变量中就实现了主调函数到被调函数间的数据传递。函数间参数传递的两种方式 传值方式 传地址方式 4.1.2 值参数传递的函数调用 函数调用时实参的种类实参是值参数变量常数表达式实参是地址值参数指针变量变量的地址(&a)数组名或某数组元素地址函数名4.1.2 值参数传递的函数调用 例 4.3 传值方式#include void main()int a=10,b=5; void swap(int x, int y); swap(a,b); /*函数调用语句*/ printf(“swap调用后:a=%d,b

10、=%dn”,a,b);void swap(int x, int y)int temp; temp=x,x=y,y=temp; printf(“swap调用中:x=%d,y=%d”,x,y);函数定义:一个函数实体函数声明语句4.1.2 值参数传递的函数调用 为被调函数的形参变量(局部变量)分配存储;实际参数拷贝到对应形式参数(拷贝完成后形参与实参无关);控制转到被调函数执行;程序控制流程从被调函数返回主调函数(系统自动撤消为被调函数建立的形参局部变量);105abyxtemp10510510函数调用执行过程与参数传递 swap函数的功能是交换两个参数的值。但运行结果表示,它只交换了两个形参变量

11、x和y的值,而没有交换main()中两个实参a与b的值。 调用时形参y形参x105105实参a实参b形参y形参x实参a实参b105510交换后图4.2程序运行结果:swap调用前:a=10,b=5swap调用中:x=5,y=10swap调用后:a=10,b=54.1.2 值参数传递的函数调用 例4.3 传值调用重要结论: 函数的传值调用方式是一种数据复制方式。在这种方式下,实参值通过复制的方式传递给形参变量,实参与形参各自占用内存不同的存储单元,当数据传递结束后,它们互不相干。 因此,在被调函数中无论形参怎样变化,都不会影响主调函数中实参的值。 函数受外界影响减小到最小限度,从而保证了函数的独

12、立性。 4.1.2 值参数传递的函数调用 4.1.3 指针与地址值参数传递的函数调用 如果要在被调函数中对主调函数的实参单元进行操作,则需要将主调函数中实参的内存地址值传递给被调函数对应的形参单元。 在传地址方式下,被调函数中用于接收对应地址值的形参必须是指针变量或数组。 本小节主要介绍指针变量的基本用法和地址值参数传递的函数调用。1)指针与指针变量的概念指针(一个变量的地址)指针就是地址。在程序设计中,系统根据变量定义的数据类型为其分配一定长度的内存单元。一旦分配完成,变量与内存单元的首地址就建立了一种对应关系(见图1)。指针变量(用来存放另一变量的地址) 内存单元地址是用有序整型数(无符号

13、整型数)进行编址。为了能够操作这些地址量,有必要构造一种变量来存储它们,这种变量称为“指针变量”(见图2)。4.1.3 指针与地址值参数传递的函数调用变量和地址的对应关系(图1) 房间变量名 房客变量值 房间号地址 short i,j; char ch; float f; i=5; j=3; ch=H; f=3.14; 53H3.14ijchf2000200220042005200920012003200620072008编译或函数调用时为变量分配内存单元内存中每个字节有一个编号地址变量是对程序中数据存储空间的抽象指针与指针变量的概念(图2).2000200420062005整型变量i20变量

14、ptr2001200220032000指针 指针变量 变量的内容 变量的地址 指针变量ptr变量ii的地址200020指向i地址存入指针变量指针变量的定义数据类型符 *指针变量名1,*指针变量名2 ; int i,j,*p1, *p2; float f; float *pf;合法标识符表示定义指针变量不是*运算符注意:int *p1,*p2; 指针变量名是p1,p2 ,不是*p1,*p2。指针变量只能指向定义时所规定类型的变量。指针变量定义后,变量值不确定,使用前必须先赋值。指针变量所指向目标变量的数据类型2)指针变量的赋值与初始化 int x, *y=&x; float *j=&x; voi

15、d *p=&x 指针变量只能指向与它同类型的普通变量,但任何类型变量的地址可赋值给 void型(空类型)的指针变量。例 int *p=&i; 无确定地址 int i; p=1000 ; 只能存放地址例 int x,*y; y=&x;指针变量赋值的一般形式 初始化方式为指针变量赋值 数据类型符 *指针变量名=初始地址值; 指针变量赋值的一般形式:指针变量名=地址值; 其中地址值表示为:&例 int *p; p=NULL; p=0;为指针变量p赋空值& &为取地址运算符,作用是返回操作对象的地址。 * *为指针运算符(或称为间接运算符),作用是返回以操作对象的值作为地址的内存单元里的内容。 xy1

16、002500025000int x, *y=&x; y &x *y xscanf(“%d”,&x); scanf(“%d”,y);printf(“%dn”,x); printf(“%d”,*y);x=100; *y=100; *&x=100; (*与&同级,从右到左结合)3)指针变量的引用例4.4 程序演示指针变量作为函数的参数 使用指针变量作为函数的参数实现的是传地址值调用,其主要特征为:主调函数中,以数据对象存放的地址值(地址或指针变量)作为实参调用另外一个函数;被调函数的形参必须是可以接收地址值的指针变量;实参和形参数据类型必须相同 传地址值调用与传值调用的区别P1(形参) a地址a地址

17、a地址*p1a P 重要结论传地址值调用时,数据在主调函数和被调函数中均使用同一存储单元,所以在被调函数中对数据任何的变动必然会反映到主调函数中。4) 地址值参数传递调用a地址/*指针变量作参数*/void swap(int *x,int *y) int t; t=*x; t=a; *x=*y; a=b; *y=t; b=t;/*普通变量作参数*/void swap(int x,int y) int t; t=x; x=y; y=t;105105510abyxabxya地址b地址a地址b地址*x*ya地址b地址tt4) 地址值参数传递调用例4.3 值传递例4.5 地址值传递当函数调用传递的是地

18、址值时,能否在被调函数中改变主调函数中实参的值取决与在被调函数中对参数的操作方式:在被调函数中若操作形参指针变量所指向的对象,则可以在被调函数中改变主调函数中的实参值。在被调函数中若操作的是形参指针变量本身,则仍然实现的是传值调用,并不能改变主调函数中的实参值。5)指针变量与被指针指向变量的区别/*被调函数中操作指针指向的对象*/void swap(int *x,int *y) int t; t=*x; *x=*y; *y=t;操作指针变量指向的对象,因而可以改变主调函数中实际参数的值。/*被调函数中操作指针变量本身*/void swap(int *x,int *y) int *t; t=x;

19、 x=y; y=t;操作指针变量本身,因而不能改变主调函数中实际参数的值。5)指针变量与被指针指向变量的区别例4.6 程序演示105abyxta地址b地址a地址a地址b地址操作指针变量指向的对象交换或改变了实参的值。操作指针变量本身,交换了实参的地址,不改变实参值5)指针变量与被指针指向变量的区别4.1.4 数组参数传递的函数调用 在C程序中,既可以用数组元素作为函数的参数,也可将数组整体作为函数的参数。使用数组元素作为参数传递,是实现函数间的传值调用。将数组整体作为参数传递时,用数组名或某数组元素的地址作为函数的实参,实现的是函数间的传地址值调用。4.1.4 数组参数传递的函数调用/* ex

20、04-07.cpp */#include #include #include #define N 5void main()void myprint(int x);int aN,bNN,i,j;srand(time(NULL);printf(数组a.n);for(i=0;iN;i+)ai=rand()%100;myprint(ai); printf(数组b.n); for(i=0;iN;i+) for(j=0;jN;j+) bij=rand()%100; myprint(bij); printf(n); void myprint(int x) printf(%4d,x); 例4.7 数组元素作值

21、传递2340一维数组作函数的参数 数组名或某元素地址作为函数的实参,实参数组将它的全部或部分存储区域提供给形参数组共享,即形参数组与实参数组是同一存储区域或是实参数组存储区域的一部分。当函数调用结束后,形参数组消失,主调函数的数组就保存了形参数组操作的结果。 使用数组名或数组第一个元素的地址是把整个实参数组传递给被调函数。 使用某个数组元素的地址作为主调函数的实参可以实现传递部分数组元素到被调函数。abs / *sv /*va或&a0 作实参b+i或&bi 作实参4.1.4 数组参数传递的函数调用4.1.4 数组参数传递函数调用例4.8 编制求和函数并通过该函数求数组的各元素和。数组名a作实参

22、数组v为形参数组a和v共享全部存储单元。#include #define N 10void main() int sum(int v,int n); int total; int aN=1,2,3,4,5,6,7,8,9,10; total=sum(a,N); printf(total=%ldn,total);int sum(int v,int n) int i,s=0; for(i=0;in;i+) s+=vi; return s;例4.8 数组名作地址传递4.1.4 数组参数传递函数调用 例4.9 编制求和函数并通过该函数求数组自某一元素后的所有元素和,起始点元素序号从键盘输入。/* Na

23、me:ex04-09.cpp */#include #define N 10void main()int sum(int v,int n); int total,pos; int aN=1,2,3,4,5,6,7,8,9,10; printf(请输入求和起始元素序号: ); scanf(%d,&pos); total=sum(&apos,N-pos);printf(total=%ldn,total);int sum(int v,int n) int i,s=0; for(i=0;in;i+) s+=vi; return s;部分元素的地址传递&apos是将数组自apos元素以后提供给形参v共享

24、,N-pos是共享数组元素个数7374.1.4 数组参数传递函数调用数组的起始地址数组的起始地址a 平面起始地址(二级地址)&a00 线性起始地址(一级地址)a0 线性起始地址(一级地址)*a 线性的起始地址(一级地址)图4.8 二维数组起始地址的表示 二维数组作函数的参数 二维数组存储时也是有序地占用一片连续的内存区域,数组名表示这段存储区域的首地址。 注意:二维数组的起始地址有多种表示法,有平面起始地址和线性起始地址之分。 二维数组起始地址的等价表示:aa0&a00*a aa0a1a2a3&a004.1.4 数组参数传递函数调用使用二维数组名作为实参 用二维数组名作为函数参数实现的是“传地

25、址调用”,其本质是实参数组将它的全部存储区域提供给形参数组共享,即形参数组与实参数组是同一存储区域。实参a形参b5图4.9 实参为二维数组名例4.10 程序演示4.1.4 数组参数传递函数调用使用二维数组起始地址的一级地址形式作为实参 在实际程序设计中,有时需要函数能够处理任意行列长度的二维数组,则用二维数组作为形参的设计形式就不适合。 为了编制较为通用的函数,可使用二维数组作为实参,一维数组作为形参。如图所示:实参a0形参v形参数组v本质上是指针变量图4.10 形参数组与实参数组全部共享存储区域4.1.4 数组参数传递函数调用实现参数传递时的注意点:函数调用的实参必须是一级地址形式,同时将二

26、维数组的行数和列数传递到被调函数中。在被调函数中将传递来的二维数组当作一维数组处理。其二维数组元素在线性连续存储单元中的排列序号,即对应的一维数组元素如下: aijvi*n+j例4.11 程序演示序号函数与程序结构函数的定义和调用函数的嵌套调用和递归调用变量的作用域和生存期编译预处理多源文件C程序的组织方法4.2 函数的嵌套调用和递归调用 4.2.1 函数的嵌套调用 C语言规定:在C程序中所有的函数都是平行的。即在C程序中函数不能嵌套定义,但函数可以嵌套调用,即一个函数在被调用的过程中又调用了另外的一个函数。一个两层嵌套函数调用的过程如下图所示: 被嵌套调用函数4.2.1 函数的嵌套调用4.2

27、.1 函数的嵌套调用 例:计算s=1k+2k+3k+Nk 程序设计思路:可以把问题分解为两个模块:求幂次方模块和求和模块,在求和模块中要包含求幂次方模块。 f2()函数的参数为n和k,其返回值是n的k次方。 f1()函数的参数为n和k,返回值是幂次方的累加和。4.2.1 函数的嵌套调用#include long f2(int n,int k) long power=n; int i; for(i=1;ik;i+) power *= n; return power;long f1(int n,int k) long sum=0; int i; for(i=1;i=n;i+) sum += f2(

28、i, k); return sum;void main()int n,k; scanf(“%d,%d”,&n,&k); printf(Sum of %d powers of integers from 1 to %d = ,k,n); printf(%dn,f1(n,k);例4.12 程序演示4.2.2 函数的递归调用 一个函数直接地或间接地自己调用自己,称为函数的递归调用。 直接递归调用:调用函数在函数体内直接调用该函数自身。 间接递归调用:调用函数调用其他的被调函数,最后又由被调函数调用到该函数自己 递归调用可以看成是一种特殊的嵌套调用,它与一般的嵌套调用有明显的不同点: 4.2.2 函数

29、的递归调用 每次嵌套调用的函数都是该函数本身;嵌套调用不会无限制进行下去,总会在某种条件下结束;每次调用时,本次的函数体并没有执行完毕。故必须依靠系统提供一个特殊部件(堆栈)存放未完成的操作,以保证当递归调用结束时不会丢失操作。在递归调用时将递归过程中未执行的操作依次从堆栈栈底开始存入(简称压栈),当递归结束时再依存放时的相反顺序将它们从堆栈栈顶弹出(简称出栈),系统用堆栈指针指示应该存入和取出数据的位置。 堆栈:一段先进后出的内存区域。4.13 递归调用例,反向输出输入字符串。#include void main()void reverse();reverse();void reverse(

30、)char ch;ch=getchar();if(ch=#) putchar(ch);else reverse(); putchar(ch); 输入数据:ab#数出结果:putchar(a)putchar(b)#ba4.2.2 函数的递归调用例4.13 程序演示 ch=a; if(ch=#) putchar(ch); else reverse(); putchar(); ch=#ch=cch=bmain()输出a输出b输出c输出#函数的递归调用4.2.2 函数的递归调用 例 用递归方式求n!。 递归公式: n!=n*(n-1)! (n1)递归结束条件: n!=1 (n=1) 递归算法描述:n2

31、 递归公式n=1或n=2f(n)return 1return f(n-1)+f(n-2)TF图4.13 菲波拉契数列算法例4.15 程序演示4.2.2 函数的递归调用通过三个示例的分析,递归方式的实现也是基于C语言的条件控制结构。递归函数设计的基本框架是相对固定的。 其一般形式描述如下: if 递归结束条件成立 Return 已知结果 else 将问题转化为同性质的较简单子问题; 以递归方式求解子问题(递归公式);函数与程序结构函数的定义和调用函数的嵌套调用和递归调用变量的作用域和生存期编译预处理多源文件C程序的组织方法 一个变量的性质可以从两方面进行分析: 变量的作用域:变量能够起作用的空间

32、范围。 变量的生存期:其变量值存在的时间范围。 它们的基本意义如下:一个变量在某个函数、某个源程序文件或某几个源程序文件范围内是有效的,则称其有效的范围为该变量的作用域,在此范围内可以访问或引用该变量。一个变量的值在某一时刻是存在的,则认为这一时刻属于该变量的“生存期”,或称其在此时刻“存在”。4.3 变量的作用域和生存期 为了能够有效地确定变量的作用域和生存期两项属性,C语言用存储类别对变量进行限定。 变量定义的完整形式为:存储类别符 变量表; 其中:数据类型说明变量的取值范围及允许的操作;存储类别用于指定变量在内存中的存放方法。变量的存储类别有四种自动型(auto)寄存器型(registe

33、r)静态型(static)外部参照型(extern)4.3 变量的作用域和生存期 可在程序的三个位置定义变量:函数内部、函数的参数定义和函数外部。按变量的作用域分类 全局变量(外部变量):指定义在所有函数外的变量,其作用范围从定义处开始到源文件结束为止。全局变量的定义形式: extern 变量表; 局部变量(自动变量):有三个位置可定义函数形参;函数体内部;复合语句内部。其作用域限定在定义范围内。局部变量的定义形式: auto 变量表;4.3.1 变量的作用域全局变量与局部变量的关系int x;void main()void f1() int x; int x; void f2()外部变量(i

34、nt x)在本源程序文件中没有定义!xxx4.3.1 变量的作用域例4.18 全局变量的作用域4.3.1 变量的作用域局部变量(自动变量)的作用域 局部变量的建立和撤消由系统自动进行。如在某函数中定义了自动变量,只有该函数被调用时,系统才为函数范围内的局部变量分配存储单元;当函数执行完毕,自动变量被系统自动撤销。在变量定义中省略auto的均为自动变量。 自动变量的值在一个函数的两次调用之间不会保留。 不同函数中定义的同名局部变量之间是毫无关系的。4.19 程序演示4.20 程序演示4.3.1 变量的作用域同名全局变量与局部变量作用域重叠问题 即在某些特定的情况下,出现全局变量、函数内定义的局部

35、变量、复合语句中定义的局部变量名字相同的现象。即在全局变量与局部变量作用域重叠的情况下,C语言规定按“定义就近原则”来引用变量。 如果定义的全局变量与函数中局部变量同名,程序进入函数内,使用定义的同名局部变量。 在复合语句中如果定义有与较大范围(函数局部或全局)变量同名的变量,则使用该小局部范围内定义的同名局部变量;同名全局变量与局部变量作用域重叠问题4.21 程序演示0 x=20 x=10 x=0用户使用内存存储空间的情况程序区静态存储区动态存储区用户区所有的全局(外部)变量及静态局部变量都存放在静态存储区, 程序执行完毕才释放。函数的形参局部(自动)变量4.3.2 变量的生存期 程序运行中

36、,不同存储类别的变量,占用的存储区域不同,分配的存储时间(生存期)也不同。按变量的生存期分类 局部变量(自动变量)的生存期:这类变量存储于内存的动态存储区,它在程序运行中使用到该变量的时间段存在。即程序进入该函数或复合语句时才分配存储空间,当该函数或复合语句执行完后存储空间被撤销。 全局变量或静态变量(全局或局部)的生存期:这类变量存储于内存的静态存储区,它在编译时分配存储空间,在程序运行的整个期间都存在。4.3.2 变量的生存期 在C程序设计中,为了合理选择变量的存储类别,有必要对不同存储类别的变量在程序中的作用分两方面进行讨论。全局变量的存储类别 对于全局变量而言,能够起作用的存储类别为e

37、xtern 和 static。局部变量的存储类别 对局部变量能够起作用的存储类别为auto 和 static。1)全局变量的存储类别extern扩展作用域static 限制作用域限制强于扩展,即若两种存储说明对某全局变量同时出现,以限制说明为准。int x;static int y;extern int x;extern int x;extern int y;yxxy是静态全局变量不能扩展作用域X原作用范围源文件扩展x源文件扩展x使用extern声明,可扩充全局变量在一个源程序的作用域例4.22 程序演示1)全局变量的存储类别X原作用域X被扩充后的作用域100110130 自动变量的生存期与其

38、所在函数被调用运行的时间相同,并且自动变量的值在函数的多次调用中都不会保留。 为满足在函数的多次调用中,局部变量仍能在保持原来值基础上继续使用,C语言提供了静态存储类别(static)。 静态局部变量的定义形式: static 数据类型符 变量表;2)局部变量的存储类别静态局部变量具有如下特点:静态局部变量的存储空间在程序的整个运行期间是固定的。系统在编译时就为它分配存储空间,它的生存期是整个程序的运行期间。静态局部变量的初始化是在程序编译时进行的。如果在定义时没有对它进行初始化,那么系统将它自动初始化为0(整型)、或0(字符型)。静态局部变量的值在函数多次调用中具有可继承性。静态局部变量的值

39、只能在定义它的局部范围内使用。在它的作用域范围之外,该静态局部变量虽然存在,但不能对它进行访问。2)局部变量的存储类别例4.23 静态局部变量与自动变量的比较1 /* Name: ex04-23.cpp */2 #include 3 void main()4 void f1();5 f1();6 f1();7 8 void f1()9 int a=10;10 static int b=10;11 a+=100;12 b+=100;13 printf(a=%d,b=%dn,a,b);14 例4.23 程序演示2)局部变量的存储类别110110a=110,b=110110210a=110,b=21

40、0b+;函数与程序结构函数的定义和调用函数的嵌套调用和递归调用变量的作用域和生存期编译预处理多源文件C程序的组织方法编译预处理概念编译预处理是C编译系统在对C源程序进行编译之前对它进行的一些预加工,然后再将处理的结果和源程序一起进行编译,以得到目标代码。恰当地使用C语言的编译预处理功能可以有效地提高程序开发效率。编译预处理语句以#号开头、占用一个单独的书写行、语句的结尾不需要用“;”作为结束符。编译预处理语句可以出现在C源程序的任何位置,其作用范围是从出现点开始到源程序末尾。编译预处理语句常有三种形式宏定义文件包含条件编译4.4 编译预处理 宏定义分为代参数的宏定义和不代参数的宏定义不代参数的

41、宏定义 定义形式:#define 宏标识符 字符串 调用形式: 宏标识符(直接用在表达式中) 宏定义的作用:在宏定义的作用范围之内,将所有的宏标识符用指定的字符串替换。 字符串:可以是字符串常量、已定义的宏、表达式或语句组成的字符串。 注意: 使用#define 宏标识符 语句可撤消宏定义。4.4.1 宏定义4.4.1 宏定义例4.24 宏定义预处理/* Name: ex04-24.cpp */#include void main() double circum(); double area(); printf(“circum=%fn,circum(); printf(area=%fn,are

42、a();double circum() return 2.0*PI*R; double area() return PI*R*R; 例4.24 程序演示不进行宏代换的情况宏名出现在一个标识符中例:#define loc 12345 int local; int 12345al; (不会进行这种替换)宏名出现在字符串常量中例: #define PI 语句 printf(“The value of PI is: %fn”,PI);结果是:The value of PI is: 4.4.1 宏定义此处不替换4.4.1 宏定义例4.25 宏调用替换问题的理解/* Name: ex04-25.cpp *

43、/#include #define N 2#define M N+2#define MN 2*Mvoid main()int x=MN;printf(x=%dn,x);错误理解:N2M4(2+2)MN8(2*4)输出结果:x=8正确理解:MN2*N+2MN2*2+2正确结果:x=6 程序演示带参数的宏定义 定义形式:#define 宏标识符(形参表) 表达式调用形式:宏标识符(实参表) 宏调用的作用:将所有的宏标识符用指定的表达式替换并且用实际参数代替表达式中的形式参数。 注意:为了避免当实际参数是表达式时引起的宏调用错误,最好将宏定义中表达式样式字符串的形式参数用圆括号括起来。4.4.1 宏定义4.4.1 宏定义例4.26 代参数宏定义使用示例/* Name: ex04-26.cpp */#include #define S(r) PI*r*r #define S(r) PI*(r)*(r) void main()double a,b,area1,area2;a=3.3;b=3.2; area1=S(a); area2=3.14159*(a+b

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论