大学计算机程序设计基础(C语言3)ppt.ppt_第1页
大学计算机程序设计基础(C语言3)ppt.ppt_第2页
大学计算机程序设计基础(C语言3)ppt.ppt_第3页
大学计算机程序设计基础(C语言3)ppt.ppt_第4页
大学计算机程序设计基础(C语言3)ppt.ppt_第5页
已阅读5页,还剩148页未读 继续免费阅读

下载本文档

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

文档简介

程序设计基础 (国家级精品课) 张长海 程序设计基础 (国家级精品课) n第七章 指针 n第八章 表单数据组织结构体 n第九章 再论函数 第七章 指针 n指针与变量 n指针运算 n指针与数组 n指针与字符串 n指向指针的指针 n本章小结 作业: 7.6 7.8 7.9 7.10 7.13 7.16 习题集:P8520 练习: 7.1-7.5 7.7 7.11 7.12 7.14 7.15 7.17 n指针是高级程序设计语言中一个重要的概念 n正确灵活运用指针 可有效地表示和使用复杂的数据结构 可动态分配内存空间,节省程序运行空间, 提高运行效率 n不正确理解和使用指针,指针将是程序中最危险的 成分,由此带来的后果可能是无法估量的。 必须清楚:数据在内存中的存储和访问方式 已知:每个变量 在计算机内存占用一块存储区 该存储区的地址就是相应变量的地址 变量可能有值 该存储区保存的内容就是相应变量的 值 5 V 0F000 : 变量地 址 变量名 变量值 变量的存储 区 7.1 指针与变量 n例如有变量声明 char c=S; int v=27,u=32; int *p = 则编译程序分别给变量 c 、v 、u 、p 分配存储空间如图所示 B804 B800变量p B80232变量u B80027变量v B7FFS变量c 地址 内存变量名 变量c被分配在B7FF开始的 一个字节的内存区域中。B7FF 是该存储区域的地址,同时也 是变量c的地址,也称B7FF是 变量c的指针;该内存区域中存 储的字符S是该存储区域的内 容,也是变量c的值。 变量v被分配在B800开始的 两个字节的内存区域中。B800 是该两个字节的存储区域的首 地址,同时也是变量v的地址, 也称B800是变量v的指针;该 内存区域中存储的整数27是该 存储区域的内容,也是变量v的 值。 变量p被分配在B804开始的 四个字节的内存区域中。B804 是其首地址,同时也是变量p的 地址,也称B804是变量p的指 针;该区域中存储的B800是是 变量p的值。p是指向int类型变 量的指针类型变量,目前它指 向变量v ,所以变量p的值是v 的地址,也就是B800 ,称p指 向v 。 n必须理解清楚: 给变量分配的内存区域 该内存区域的地址 该内存区域保存的内容 以及它们之间的关系 n一个变量v的指针或称指向变量v的指针 就是给它分配的内存区域的地址 或说是给它分配的内存区域的地址首地址 n访问变量 直接访问:直接使用变量v的名字 v*10:用变量v的值(27)乘以10,得270 n间接访问:使用指向相应变量的指针 访问变量 v 可以用下面方式来实现: *p (*p)*10 :通过指向 v 的指针变量 p ,采用间接 访问的方式实现对变量 v 的访问,取出变量 v 的值 参与运算。同样得到值270 。 地址与指针是同意语。变量的指针就是变量的地 址,存放变量地址(指针)的变量是指针变量 7.1.1 指针类型与指针变量 指针类型和基类型: 在C中,任何一个类型都伴随着一个指向本 类型变量的指针类型 表现形式 设有类型T,则指向T类型变量的指针类 型用: T * 表示,T称为该指针类型的基类型。 指针变量: n意义 指针变量简称指针 是一种特殊的变量 它里面存储的“值”被解释成为一个变量的地址 ,确切的说是计算机内存的一个地址。 n声明指针变量 说明指向T类型变量的指针变量使用形式: T*p , *p , *p ; 其中,每个p都是标识符,是被说明的指针类型的变 量,确切的说是“指向T类型变量的指针变量”。 n指针所指向的类型可以是 基本数据类型 构造型数据类型 指针类型 函数 n经常简称 “指向T类型变量的指针变量v” “v指向T类型” “T类型的指针v” n例如 int *iptr1, *iptr2 ; /* 说明指向int类型变量的指针变量 iptr1和iptr2 */ char * cptr ; / 说明指向char类型变量的指针变 cptr int x , y ; char ch = a; n指针变量的值是内存地址(宏观上讲是变量的地址) n求取不同类型变量或常量地址的表达方式不同: 基本类型变量、数组成员、结构体变量、公用 体变量等,用求地址运算符“ char * cptr; int x , y ; char ch = a; 在此基础上,有操作: iptr1 = cptr = x = 5; iptr2 = y = 8; 内存单元地址变量 E990指针iptr1 E994指针iptr2 E998 指针cptr E99Cint型变量 x E99Eint型变量 y E9A0char型变量 ch E99C E99E E9A0 a 5 8 如果再执行 iptr1=iptr2; E99E 7.1.2 指针所指变量 n指针变量和指针所指变量是两个不同的概 念 指针变量即指针,它本身存储某个内存地址 (某个变量的地址)。 指针所指变量为指针变量中所保存的内存地 址对应的变量。 地址内存 变变量 jEAB 6 3 变变量 iEAB 4 EAB4 变变量 iptrEAB 0 程序片段 int * iptr; int i=3 ,j ; iptr = 运算符“ * ”访问指针 所指变量的内容,称 为间接引用 运算符。 语句: j = *iptr 将把iptr所指内存单 元(EAB4)中的内 容送入变量j ,此时j 的值为3。 *iptr表示指针变 量iptr所指变量的内 容。 3 void main() /* 2 */ int i,j; /* 3 */ int *pmax,*pmin,*p; /* 4 */ printf(“Input an integer:“); /* 5 */ scanf(“%d“, /* 6 */ printf(“Input an other integer:“); /* 7 */ scanf(“%d“, /* 8 */ pmax= /* 9 */ pmin= /* 10 */ if (i 、= 、 void main( ) char str255 , *p; int v ; scanf(“%s“,str); p=str; while( *p!=0 ) p+ ; printf(“The string length is %d n“; p-str); 程序运行若输入: abcdef 则输出结果为 The string length is 6 运行结果演示 7.3 指针与数组 n密切的关系 数组名是数组的首地址,即a0的地址; 指针值也是一个地址如果一个指针p指向数 组a的首地址即指向a0,则p与a表示的是同 一个对象。 n事实上,在C中把指针和数组当作同一个概念看 待,数组名是指针,指针也是数组。 n可以认为数组名是常量指针。 7.3.1 用指针标识数组 n例如 int a5; int *iptr iptr=a; / 也可以使用 iptr= int i , *p; for(i=0;i 0 ) k=j; temp= arr_stri; arr_stri= arr_strk; arr_strk=temp; void out_string(char *arr_str,int n) int j; for ( j=0;j *str2 ) / 比较当前字符 return 1; else if ( *str1 *str2 ) / 比较当前字符 return 1; / str1长或str1当前字母大于str2 else if ( *str1 catalogue);-catalogue); while(nowhile(no-cataloguecataloguecatalogue);-catalogue); printf(“npleaseprintf(“nplease input input bookno.orderbookno.order:“);:“); scanf(“%d“,-order); /* 检索函数 */ void searchbook(struct bookno no2) int k; for ( k=0; kname ); printf( “AUTHOR:%sn“, card0-author); printf( “LANGUGE:%sn“, card0-languge ); printf(“Publish date:%d.%d.%dn“ ,card0-publishingdate.year ,card0-publishingdate.month ,card0-publishingdate.day); printf( “ABSTRACT:n“ ) ; printf( “%s“,card0-abstract) ; printf(“n“); 运行结果演示 8.2 保存图书卡结构体 程序例8.1中使用结构体保存成分类型不同的图书卡片数据 。 类型struct bookcard有六个不同类型的成分,分别保存图 书卡片的六个成分; 类型struct date是struct bookcard类型的成分类型,有 三个成分,都是整数类型,分别保存图书卡片中出版日期的年、 月、日; 类型struct bookno也是struct bookcard类型的成分类 型,有两个不同类型的成分,分别保存图书卡片中书号的类号和 序号。 struct bookcard / 检索卡结构体类型 char name32,author16; enum class_language languge ; struct date publishingdate ; struct bookno no ; char abstract256; groupcard100 ; struct date / 日期结构体类型 int year,month,day ; ; struct bookno / 书号结构体类型 char catalogue ; int order ; ; 与数组一样,结构体也是变量的集 合,但其中成分变量有可能类型不同。 结构体类型的特点是: 一个数据项由多个子数据项组 成,而且每个子数据项的类型可能 不一样。 在人事档案管理中,每个人的自然情况表可 能包含:名字(字符串型)、年令(整型)、出 生时间(三个整型)、性别(枚举)等等。 名字年龄龄出生时间时间 年 月 日 性别别: 研究人造卫星,每个人造卫星的信息可能包括:名字(字 符型)、发射时间(三个整数)、重量(实型)、直径(实型 )、轨道半径(实型)、与赤道夹角(实型)等等。 名字发发射时间时间 年 月 日 重量直径轨轨道半径与赤道夹夹角 在程序设计语言中使用结构体描述这一类由不同类型子数 据项组成的数据。 8.2.1 定义结构体类型 n结构体类型 结构体类型是分量的集合。 分量也称成员、成分、域, 分量类型可以不同。 “结构体类型定义”呈如下两种形式: 结结构体类类型定义义形式A结结构体类类型定义义形式B struct t id,. ,id ; . t id,. ,id ; struct sid t id,. ,id ; . t id,. ,id ; struct是保留字,引导一个结构体类型定义 每个 t 是一个类型说明符,可以是任意类型的任何形式的 类型说明符。它说明后边诸标识符 id 的类型 每个 id 是一个成员声明符,具体声明结构体类型的一个 分量,它最终涉及的标识符是该分量的名字;要求在整 个结构体类型定义内,诸 id 中声明的各个分量的名字互 不相同;每个id的类型是它前边的t表记的类型。 sid是一个标识符,称结构体标签,起标记该结构体类型作用. 例8-2 一个人的自然情况表 例8-3 卫星数据类型 enum sext male , female ; struct date int year ,month ,day ; ; struct preson char name10 ; int age ; enum sext sex ; struct date birthdate ; ; struct mansatellite char name10 ; struct date lounchdate ; float weight, diameter, orbitrad, angle ; ; year: month: day: date是一个结构体类型,包含三个成分 成分year为int类型 成分month为int类型 成分day为int类型 bookno有两个成分 catalogue: 字符 order: 整数 bookcard有六个成分。 其中嵌套两个结构体类型成分: publishingdate、bookno name:字符数组组 author:字符数组组 languge:枚举举 year: publishingdate:month: day: no: catalogue: order: 类类号 序号 abstract: name:字符数组组 age: sex: year: birthdate: month : day: preson有四个成分, 成分name为数组类型, 成分age为int类型, 成分sex为枚举类型, 成分birthdate仍为一个结构体类型 name:字符数组组 year: lounchdate : month : day: weight: diameter: orbitrad: angle: 与其它构造型类型一样,结构体类型也可以嵌套 mansatellite有六个成分 n结构体类型引用 在struct后跟以结构体标签,称为“结构体类型引 用”。在例11-1的结构体类型定义的意义下: struct date struct preson struct mansatellite 都是结构体类型引用,使用它们将分别标记相应 结构体定义。 n结构体类型说明符: 结构体类型定义和结构体类型引用统称“结构体 类型说明符”。使用结构体类型说明符可以 l 定义结构体类型的类型名, l 还可以声明结构体类型变量。 8.2.2 结构体类型名 与一般类型一样,使用 typedef 可以定义 结构体 类型名。形式是: typedef 结构体类型说明符 标识符 例8-4 定义结构体类型名 typedef struct date int year,month,day ; datetype ; typedef char tstring1010; typedef struct tstring10 name ; int age ; enum sext sex ; struct date birthdate ; presontype ; typedef struct mansatellite mansatellitetype datetype birthdate; 8.2.3 结构体变量 n结构体类型变量声明可以采取如下三种形式之一 使用结构体类型引用 直接使用结构体类型定义 使用typedef定义的结构体类型名 例:结构体变量声明 struct preson zhang ; struct date int year,month,day ; dateofbirth; struct char author10 ; datetype publish_date ; int page_number ; programming ; mansatellitetype first_east ; 变量zhang用结构体类型引用声明 是struct preson类型,具有如图结构 name:字符数组组 age: sex: year: birthdate:month : day: 变量dateofbirth使用完整的结构体类型定义声明,具有如图结构 year: month : day: 变量programming使用不带结构体标签的结构体类型定义声明, 包含3个成分,分别为字符数组类型的author、datetype类型的 publish_date、int类型的page_number,结构如图所示; author: year: publish_date: month : day: page_number : 变量first_east使用typedef定义的类型标识符 mansatellitetype声明,具有如图结构 name:字符数组组 year: lounchdate:month: day: weight: diameter: orbitrad: angle: 类型定义不分配存储空间,只说明一个数据类型 的框架结构。只有到变量声明时才给变量分配存储空间 ,并且使得被声明的变量具有相应类型的结构。 到目前为止本章定义的所有标识符,只有本节声 明的四个变量zhang、dateofbirth、programming、 first_east具有实体,被分配存储空间。 类型标识符只是定义了一个数据类型的框架,不 占用存储空间,只给相应类型起一个名字。 8.2.4 指向结构体变量的指针 n任何类型都伴随一个指向相应类型的指针类型, 并可以声明相应指针类型的变量,结构体类型当 然不例外。 输出检索结果函数out_anser中的card0就是一个指向结构 体变量的指针变量。使用形式 struct bookcard *card0 声明; 在例8.1中: 输入一个书号函数输入一个书号函数 inputbooknoinputbookno中的中的nono也是一个也是一个指向结构 体变量的指针变量。使用形式 structstruct booknobookno * no * no 声明。声明。 void void inputbookno(structinputbookno(struct booknobookno * no) * no) printf(“nstartprintf(“nstart search: search:npleasenplease input input bookno.cataloguebookno.catalogue:“);:“); scanf(“%c“,-catalogue); while(nowhile(no-cataloguecataloguecatalogue);-catalogue); printf(“npleaseprintf(“nplease input input bookno.orderbookno.order:“);:“); scanf(“%d“,-order); void out_answer( struct bookcard *card0 ) printf( “NAME:%sn“, card0-name ); printf( “AUTHOR:%sn“, card0-author); printf( “LANGUGE:%sn“, card0-languge ); printf(“Publish date:%d.%d.%dn“ ,card0-publishingdate.year ,card0-publishingdate.month ,card0-publishingdate.day); printf( “ABSTRACT:n“ ) ; printf( “%s“,card0-abstract) ; printf(“n“); n在前几节声明的基础上,下述例子声明指向不同结构 体类型变量的指针变量pointer_preson、 dateofpointer、p、p_east。 struct preson *pointer_preson ; struct date int year,month,day ; *dateofpointer ; struct char author10 ; datetype publish_date ; int page_number ; *p ; mansatellitetype *p_east 例:指向结构体类型变量的指针变量 变量pointer_preson为指向标签为preson的结构体类型变量的 指针变量。 pointer_preson可以指向相应结构体类型的变量。 比如 pointer_preson = 变量dateofpointer为指向标签为date的结构体类型变量的指针 变量。 dateofpointer可以指向相应结构体类型的变量。比如 dateofpointer = 变量p为指向无标签结构体类型变量的指针变量。 P 可以指向相 应结构体类型的变量。比如 p = 变量p_east为指向结构体类型mansatellitetype变量的指针变 量。 p_east 可以指向相应结构体类型的变量。比如 p_east = 8.2.5 访问结构体变量的成分 访问结构体变量的一个成分,使用成员选择 表达式。包括“直接成员选择”和“间接成员选择” 两类 n直接成员选择 直接成员选择表达式针对一般的结构体变量。 形式是: r . w r 是结构体变量; w是 r 所属结构体类型中的一个成员名字 下述成员选择表达式是合法的: programming_pascal.author n间接成员选择 间接成员选择表达式针对指向结构体变量的指针 变量 形式是 p-w p 是一个指向结构体变量的指针变量; w 是p所指向结构体变量所属类型中的一个成员 名字 下述成员选择表达式是合法的: pointer_preson - name p_east - weight p_east - lounchdate 当然也可以首先对指针变量进行求地址运算,然后使用直接成员选 择。比如上述三个选择表达式还可以写成如下形式。由于优先级的 原因,这里的括号是必须的。 (*pointer_preson).name (*p_east).weight (*p_east).lounchdate 由于成员选择表达式本身也是一个变量访问,它是相应成分类型的 一个变量,它与成分类型的其它变量一样,凡是可以使用那些变量 的地方都可以使用成员选择表达式。对于嵌套结构体,可以认为“ 成员选择表达式”仍然是一个“后缀表达式”,所以可以继续应用“成 员选择表达式”的规则访问里层的成分。例: zhang.birthdate.mouth p_east - lounchdate.year 例8-5 设计表示复数的结构体类型, 给出复数加法和乘法函数 解: /* 复数类型 */ typedef struct complex float real_part,imaginary_part ; complex_type /* 复数加法 */ complex_type complex_add(complex_type x, complex_type y) complex_type add; add.real_part=x.real_part+y.real_part; add.imaginary_part=x.imaginary_part+y.imaginary_part; return add; /* 复数乘法 */ complex_type complex_mul ( complex_type x, complex_type y ) complex_type product ; product.real_part = x.real_part * y.real_part + x.imaginary_part * y.imaginaty_part ; product.imaginary_part = x.real_part * y.imaginary_part + x.imaginaty_part * y.real_part ; return product ; 运行结果演示 本章小结 n本章讲述构造数据类型 结构体类型 结构体变量 结构体成分变量成分选择表达式 枚举类型 n重点掌握结构体应用 第九章 再论函数 n函数参数 n函数值 n作用域 n局部量和全局量 n递归程序设计 n本章小结 作业: 9.8 9.11 习题集:P9116 练习: 9.1 9.2 9.3 9.5 9.6 9.9 9.1 参数 9.1.1 传递直线方程系数指针作参数 第五章求三角形重心的例题5.2的程序,传递一个直线方程 的两个系数信息时让人感到十分别扭,函数不能把多个计算结 果带回调用处,不得不使用全局变量。使用指针参数可以解决 这个问题。 float xa,ya,xb,yb,xc,yc; /分别保存三角形三个顶点点的X、Y方向坐标 float xd,yd,xe,ye ; /分别表示中点D、E坐标 float a1,b1,a2,b2,a,b;/分别表示中线AD、BE的方程系数, a、b临时变量 float xo,yo;/重心O的坐标 /* 求中线:参数:三角形三个顶点r、s、t的x、y坐标 */ void lines( float xr,float yr,float xs,float ys,float xt,float yt ,float *a,float *b) /6 float xu,yu;/中点u坐标/7 xu=(xs+xt)/2; /求st边的中点u/8 yu=(ys+yt)/2;/9 /求过r、u两点的直线方程/10 *a=(yr-yu)/(xr-xu); /计算系数a;用指针参数带回主程序/11 *b=yr - *a * xr ;/计算系数b;用指针参数带回主程序/12 /13 void main(void) / 主函数/14 / 输入三个点的X、Y方向坐标 346 360 416 108 116 212 printf(“please input xa,ya,xb,yb,xc,yc:n“);/15 scanf(“%f%f%f %f%f%f“,/16 lines(xa,ya,xb,yb,xc,yc, /求BC边的中线AD/17 lines(xb,yb,xa,ya,xc,yc, /求AC边的中线BE/19 xo=(b2-b1)/(a1-a2); /求AD、BE交点O/21 yo=a1*xo+b1;/22 printf(“重心坐标:x=%10.3f y=%10.3f n”,xo,yo);/ 打印输出 /24 运行结果演示 函数lines用指针参数a 、b代替了原来的全局量和函 数值来传递计算结果。 在函数声明中,增加形式参数a 、b ,并把它声明成 指针类型,构成指针参数 在函数调用中,用变量的指针(地址)作实在参数, 对应相应形式参数。 在函数声明中,对形式参数a 、b以间接寻址方式 赋值,把值直接送入实在参数指针所指向的变量中。 以函数调用lines(xa,ya,xb,yb,xc,yc, temp=*xx; *xx=*yy; *yy=temp; void main() int x,y; scanf(“%d %d“, if(x *max)/ 求极大值,p指向当前数组元素 *max = *p;/ max指向极大值单元 if (*p 12) return name0 ; else return namem; 程序运行结果 Input a number of a month: 2 It is February 运行结果演示 n函数返回类型不允许是数组类型和函数类型,除此之外允 许一切类型,当然允许指针类型 n指针类型函数(带回指针值的函数)的函数定义说明符形 式是: 类型名 *函数名( 形式参数表 ) 例: float *f ( int x , int y ) f 是函数名; x 和 y 是两个 int 类型形参; 该函数的返回类型是“ float * ” 即指向 float 类型的指针。 可以这样解释该函数定义说明符。 按运算符优先级规定,“ * ” 的级别低于 “()”, f (int x, int y) 是一个函数,它的类型是 “ float *”, 即函数 f 的类型是 float 类型指针 函数f 是返回指针值的函数。 在函数内,return 语句后边的表达式类型应该是 类型名 * 比如若有声明 float u , *v ; 则,下述return语句都是正确的 return rerurn v; 而语句 return u; 是错误的。 C指针函数只允许返回全局变量指针、静态变量指针、堆内空间地 址,不允许把函数内部声明的局部变量指针(地址)作为返回值。 在例8-10中,如果把字符串数组name放在函数mp内声明就错了。 因为函数内部声明的局部变量在栈区分配空间,函数调用结束时, 释放栈区中函数运行空间,会造成返回指针所指存储空间已不存在 9.2.2 读入图书卡片返回结构体值的函数 回顾第八章例8.1中函数inputcard。 struct bookcard inputcard( void ) struct bookcard card; int k; printf(“nn new card:“); printf(“nplease input bookname:”); scanf(“%s”, ); printf(“nplease input abstract:”); scanf(“%s”, card.abstract); return card; inputcard函数的类型是struct bookcard为一个结构体类 型,表示函数返回值是一个结构体值。在inputcard返回时,使 用语句 return card; 返回。 函数带着变量card的值返回,card正好是struct bookcard类型的。函数将带着struct bookcard类型的一 个结构体值返回到主函数main中。 在主函数main中,以 groupcardi = inputcard( ); 调用函数inputcard。把inputcard带回的结构体值送入数 组成分变量groupcardi中。 n函数的计算结果可能是一个结构体值。在C中,有两种途 径能够把该结构体值通过函数名字带回调用函数的主程序 。 使用指针。函数的结果类型是指向结构体类型变量 的指针类型。 直接使用结构体类型。函数的结果类型是结构体类 型,直接把一个结构体值带回调用函数的主程序。 n第二种方式就是我们刚刚介绍的例8.1中inputcard所用 的方式。 n第一种方式就是返回指针的函数,只不过相应指针是指向 结构体类型变量的指针。与其它类型返回指针的函数没有 任何区别,就是9.2.1介绍的方法。 9.3 作用域 n所谓作用域,就是使程序中声明的标识符有定义的区域。 在一个标识符的作用域内,使用它是合法的。 n作用域是一个静态概念,描述从程序静态行文上看,程序 中一个被声明的标识符起作用的范围。下表列出各种C标 识符的作用域。 C 标识标识 符作用域 类别 声明的作用域 顶层标识顶层标识 符从声明点到本源程序编译单编译单 位文本结结束 函数定义义中的形参从声明点到函数体结结束 函数原型中的形参从声明点到函数原型结结束 复合语语句中声明的标识标识 符 从复合语语句中声明点到复合语语句结结束 语语句标标号相应标应标 号声明所在的整个函数体 预处预处 理器的宏 从相应应宏定义义的#define命令到本源程 序编译单编译单 位文本结结束, 或第一个取消相应应宏定义义的#undef命 令 在C中,每个源程序编译单位,每个函数定义、函数原型 、复合语句都各自构成一个作用域区域。C规定: l 在嵌套结构中,若里层区域的一个标识符与外层区域 的某标识符同名,则外层标识符的作用域不包括里层 那个同名标识符的作用域区域。 l C 程序中使用的任意一个标识符必须声明;并且必须 先声明后使用;在同一区域内任何标识符不得重复声 明。 例9.7 错误应用 标识标识 符 k 没有声明标识标识 符 x 在同一作用域中重复声明 int i,j; int f(void) . . k=i+j; float f( int x ) int x ; . . 第三行的 c 将被替换为换为 2.0标识标识 符 index , t2 先使用后声明 #define c 2.0 typedef int t ; t c,d,e; #define c 2.0 t2 y index ; typedef t2 float ; #define index 10 9.4 局部量和全局量 n由作用域规则可知 一个函数或复合语句引入的标识符只在本 函数或复合语句内有效, 在本函数或复合语句外,便失去了它的意 义 称它们是局部于声明它们的函数或复合语 句 或称该标识符相对于声明它们的函数或复 合语句来讲是局部量。 由作用域规则还可知 n任何顶层声明的标识符在所有函数内部以及复合语句都可 以使用。条件是在函数内部和复合语句中没有与它同名的 其它声明,称顶层声明的标识符相对于函数和复合语句来 讲是全局量 n若一个复合语句嵌套在一个函数或另一个复合语句之内, 且某一个标识符在函数或其外层复合语句中声明,在内层 复合语句中没有与相应标识符同名的声明,则函数或外层 复合语句中关于这个标识符的声明在内层复合语句中仍然 有效。即在内层复合语句中仍可使用这个标识符,称在函 数或外层复合语句中声明的标识符相对于其内层复合语句 来讲是全局量。 n全局量和局部量是一个相对概念。 某标识符相对于某复合语句是局部的,相对于 其内层复合语句却是全局的; 反之某标识符相对于里层某复合语句是全局的 ,但是相对于声明它本身的那个复合语句来讲却 是局部的。 n全局量在内层具有外层同样的意义 在内层对全局量的操作直接反应在外层程序中 例9.8 计算调合级数前项和 开始 结束 h=0 i=1 TO n h=h+1/i h 约分 /*PROGRAM the summation of a seriers*/ #include “stdio.h” /* 1 */ int a,b,n; /* 2 */ void add ( int *e , int *f , int ii ) /* 3 */ *e = (*e) * ii + (*f) ; /* 4 */ *f = (*f) * ii ; /* 5 */ /* 6 */ int gcd( int u , int v ) /* 7 */ int r ; /* 8 */ r=v; /* 9 */ while ( r!=0 ) /* 10 */ r = u % v ; /* 11 */ u = v ; /* 12 */ v = r ; /* 13 */ /* 14 */ return u ; /* 15 */ void reduce ( int *x , int *y ) /* 17 */ int g ; /* 18 */ g = gcd( x,y ) ; /* 19 */ *x = (*x) / g ; /* 20 */ *y = (*y) / g ; /* 21 */ /* 22 */ int main(void) /* 23 */ int i ; /* 24 */ printf(“please input the value of n:“); /* 25 */ scanf(“%d”, /* 26 */ a=0; /* 27 */ b=1; /* 28 */ for (i=1 ; i0 按照该定义,! 就是一个简单的条件语句和表达式计算, 可以编出如下函数: int factorial ( int n ) if ( n=0 ) return 1; else return n*factorial(n-1); 运行结果演示 问题:在函数 factorial 内又调用函数 factorial 本身,行吗? 回答:行! 1. 首先按作用域规则,在函数 factorial 内又调用函数 factorial 本身是合法的;其次 C 系统保证上述调用过程执行的正确性, 这就是递归。 2. 从静态行文看,在定义一个函数时,若在定义它的内部,又出 现对它本身的调用,则称该函数是递归的,或递归定义的。 3. 从动态执行角度看,当调用一个函数时,在进入相应函数,还 没退出(返回)之前,又再一次的调用它本身,而再一次进入 相应函数,则称之为递归,或称之为对相应函数的递归调用。 4. 称递归定义的函数为递归函数。 上述函数 factorial 就是递归函数。若计算 5! ,使用函数调用 factorial(5) ,观察其计算过程: return 5*f(4) f(5) f=120 f(4)f(3)f(2)f(1)f(0) return 4*f(3) return 3*f(2) return 2*f(1) return 1*f(0) return 1 f=24f=6f=2f=1f=1 int factorial ( int n ) if ( n=0 ) return 1; else return n*factorial(n-1); void main() printf(“%dn”,f(5) ); 运行结果演示 递归程序设计 例9.10 X 的 n 次幂,可以定义为 float power ( float x, int n ) if ( n=0 ) return 1 ; else return x * power(x,n-1) ; 运行结果演示 例9.11 n 次勒让德多项式 计算它的递归函数是: float p ( int n , float x ) if ( n=0 ) return 1 ; else if (n=1) return x ; else return ( (2*n-1)*x*p(n-1,x) - (n-1)*p(n-2,x) )/n ; 运行结果演示 n递归程序设计的思想体现在: 用逐步求精原则,先把一个问题分解成若干子 问题 这些子问题中有问题的与原始问题具有相同的 特征属性,至多不过是某些参数不同,规模比原 来小了 此时,就可以对这些子问题实施与原始问题同 样的分析算法,直到规模小到问题容易解决或已 经解决时为止。 也就是说,若将整个问题的算法设计成一个函 数,则解决这个子问题的算法就表现为对相应函 数的递归调用. 这里讲的只是一般规律和程序设计思想。实际使用时,设 计递归函数要复杂得多。编写递归程序要注意: 1. 递归程序漂亮、好看、好读、风格优美,但执行效率低 。 2. 计算! 的函数即可以写成循环形式,也可以写成递归 形式。但是有些循环程序写成递归很困难。反之,有些递归程 序写成循环也很困难,甚至是不可能的。 3. 终结条件。程序一定要能终止,不能无限递归下去。 4. 使用全局量要特别小心。用不好,单元发生冲突,将导 致程序出错。 例9.12 汉诺( Hanoi )塔游戏 n该问题又称世界末日问题。相传,古印度布拉玛婆罗门神庙 的憎侣们,当时作一种被称为 Hanoi塔的游戏。该游戏是: 在一个平板上,有三根钻石针;在其中一根针上有成塔型落 放的大小不等的64片金片;要求把这64片金片全部移到另一 根钻石针上。移动规则是: 每次只允许移动一片金片; 移动过程中的任何时刻,都不允许有较大的金片放在 较小的金片的上面; 移动过程中,三根钻石针都可以利用,但是金片不许 放在除钻石针以外的任何地方。 n不论白天黑夜都有一个憎侣在移动。据说当64片金片全部从 一根钻石针移到另一根钻石针上那天,就是世界的末日。到 那时他们的虔诚信徒可以升天,而其他人则要下地狱。 当然这只是传说,按照规则把 64 片金片全部从一根针移到另一根 针上,总的移动次数是 264-1次,若一秒移动一次,不发生错误, 日夜不停的移动,约需 5849 亿年。而太阳系的寿命仅有100150 亿年而已。 请编程序,打印金片的移动顺序。 解:不妨设三根钻石针顺次编号为 a 、b 、c ;开始所有 64 片金 片全部在 a 针上;现在要把它们移动到 b 针上;移动过程中 c 针可 以利用。如图所示 . . 64片 a b c 63片 . . 63片 . . 64片 1. 试想,若能够把a 针上的64片金片全部移动到b针上,必须能够 先把a针顶部的63片金片移到c针上。 2. 现在,可以很容易的把a针上的一片金片移到 b 针上。 3. 最后,再按照把a针上的63片金片移到c针上的算法,把c针上的 63片金片全部移到b针上。从而,完成了题目要求的工作。 重新观察上述过程: 怎样进行移动? 开始就遇到把a

温馨提示

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

评论

0/150

提交评论