《计算机基础与程序设计实践教程》课件 第8章 数组、函数、指针与文件_第1页
《计算机基础与程序设计实践教程》课件 第8章 数组、函数、指针与文件_第2页
《计算机基础与程序设计实践教程》课件 第8章 数组、函数、指针与文件_第3页
《计算机基础与程序设计实践教程》课件 第8章 数组、函数、指针与文件_第4页
《计算机基础与程序设计实践教程》课件 第8章 数组、函数、指针与文件_第5页
已阅读5页,还剩57页未读, 继续免费阅读

下载本文档

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

文档简介

第8章数组、函数、指针与文件主讲:王淞春2026年10月8日本章目录8.1数组第1学时8.2函数与模块化设计第2学时8.3指针第3学时8.4结构体和共用体第4学时·上8.5文件操作第4学时·下★本章小结与课后任务全章回顾2学习目标与教学重难点知识目标掌握一维/二维数组的定义、初始化与引用掌握函数的定义、调用、参数传递与递归理解指针的概念,掌握指针与数组、字符串的应用掌握结构体、共用体的定义与引用掌握文件的打开、读写与关闭操作能力目标能运用数组与函数解决批量数据处理问题能正确使用指针访问数组、字符串与函数能用结构体组织与管理复杂数据能编程实现文件的顺序与随机读写教学重点一维数组的初始化与引用;冒泡排序函数的参数传递与递归调用指针与数组、字符串的关系结构体变量的定义与成员引用文件的打开模式与读写函数教学难点冒泡排序的算法过程递归调用的执行流程指针的概念及相关运算二维数组的地址表示形式文件的定位与随机读写38.1数组第1学时8.1.1一维数组·8.1.2二维数组·8.1.3多维数组48.1引例:为什么需要数组问题引入统计某公司100名员工的平均工资:若使用基本类型变量,需定义100个变量——繁琐且容易出错。解决方案用x[0]、x[1]、x[2]、…、x[99]表示这批数据,通过方括号中的数字(下标)区分各个变量。数组:一组具有相同数据类型的变量的集合数组元素:数组中的每一项下标:表示元素在数组中的位置序号,从0开始数组x(10个元素,按序排列)x[0]x[1]x[2]x[3]x[4]x[5]x[6]x[7]x[8]x[9]下标从0开始,最后一个元素为x[9]图8-1数组x[10]示意构造类型:除基本数据类型外,C语言还提供构造类型——数组、结构体、共用体,本章讲解其中的数组类型。维数:数组下标的个数称为维数;按维数分为一维、二维、三维数组等,二维及以上统称多维数组。58.1.1一维数组的定义与初始化定义格式:类型说明符数组名[常量表达式];示例:intarray[5];类型说明符int·数组名array·常量表达式5即数组长度(存放元素的个数)完成定义后只是开辟了内存空间,使用前还需初始化。三种常见初始化方式:①全部元素赋值inti[5]={1,2,3,4,5};长度为5,元素值依次为1、2、3、4、5②部分元素赋值inti[5]={1,2,3};只给前3个元素赋值,其余元素默认为0③不指定长度inti[]={1,2,3,4};系统按初值个数确定长度,此处为4注意数组下标用方括号[]括起来,而不是圆括号;数组名的命名规则与变量名相同。常量表达式的值可以是符号常量:#defineN4→inta[N];是合法定义68.1.1一维数组的引用引用方式:数组名[下标];例:引用数组x的第3个元素→x[2]下标表示元素位置,从0开始下标范围:0~数组长度-1引用数组元素与使用同类型变量一样,可参与运算注意:下标不能越界假设数组长度为6,则下标范围为0~5;访问x[6]超出范围,程序会报错。C语言编译器不做数组越界检查,越界访问可能导致难以定位的运行错误,编程时须自行保证下标合法。例8-1数组的使用#include<stdio.h>voidmain(intargc,char*argv[]){

intx[5]={2,3,1,4,6};

inti;

for(i=0;i<5;i++){printf("%d\n",2*x[i]);}}▍运行结果462812先定义数组,再通过下标获取元素,乘以2后输出。7一维数组的常见操作①:遍历遍历:依次访问数组中的每个元素用for循环实现:循环变量i同时充当数组下标下标范围0~4,故循环条件为i<5遍历是数组最基础的操作:求和、求平均、查找、排序均以遍历为基础执行过程i=0→访问x[0]→输出1i=1→访问x[1]→输出2……依次类推……i=4→访问x[4]→输出5例8-2数组遍历#include<stdio.h>voidmain(intargc,char*argv[]){

intx[5]={1,2,3,4,5};

inti;

for(i=0;i<5;i++){printf("x[%d]:%d\n",i,x[i]);}}▍运行结果x[0]:1x[1]:2x[2]:3x[3]:4x[4]:58一维数组的常见操作②:求最值算法思路(打擂台法)①假定首元素为最大值:nMax=x[0];②遍历其余元素:for(i=1;i<5;i++)③若发现更大的元素,更新擂主:if(x[i]>nMax)nMax=x[i];④遍历结束,nMax中即为数组最大值求最小值:只需将比较条件改为x[i]<nMin。例8-3获取数组中的最大值#include<stdio.h>intmain(intargc,char*argv[]){

intx[5]={1,2,3,4,5};

intnMax=x[0];//假定首元素最大

inti;

for(i=1;i<5;i++)

if(x[i]>nMax)nMax=x[i];//更新最大值printf("max:%d\n",nMax);

return

0;}▍运行结果max:59一维数组的常见操作③:冒泡排序基本思想不断比较数组中相邻的两个元素:较小者向上浮,较大者往下沉——与水中气泡上升原理相似(从小到大排序)。排序步骤第一步:从第一个元素起依次比较相邻两元素,前者大则交换→一轮后最大值沉到最后第二步:除最后一个元素外,剩余元素继续两两比较→次大值放到倒数第二位第三步:依次类推,对越来越少的元素重复上述步骤,直到没有任何一对元素需要比较两个变量借助中间变量交换(图8-3)①nTemp=x[n];暂存x[n]的值②x[n]=x[n+1];后一个元素前移③x[n+1]=nTemp;暂存值归位x[n]9x[n+1]8交换x[n]8x[n+1]9图8-3交换步骤:借助中间变量nTemp完成交换关键:每轮比较确定一个最大值的位置;n个元素共需n-1轮比较,第m轮需比较n-1-m次。10例8-4冒泡排序算法实现#include<stdio.h>voidmain(intargc,char**argv[]){

intx[5]={9,8,3,5,2};

intm,n,nTemp;

for(m=0;m<5-1;m++)//外层:比较轮数

for(n=0;n<5-1-m;n++)//内层:每轮比较次数

if(x[n]>x[n+1])//前者大则交换{nTemp=x[n];x[n]=x[n+1];x[n+1]=nTemp;}

for(n=0;n<5;n++)printf("%d",x[n]);}代码要点外层循环m:控制比较轮数(长度-1轮)内层循环n:控制每轮比较次数(长度-1-m次)if语句:前者大于后者则借助nTemp交换▍运行结果冒泡排序前:98352冒泡排序后:23589双循环结构:每一轮确定一个元素最终位置,循环结束即完成排序。11冒泡排序过程演示(图8-2)初始98352第1轮83529第2轮35289第3轮32589第4轮23589橙色:本轮比较后已归位的元素(每轮确定一个最大值“沉底”)过程观察第1轮:元素9最大,每次比较都交换,最终沉到最后第2轮:过程相似,元素8放到倒数第二位第3、4轮:比较范围逐步缩小,直至全部有序规律总结5个元素→4轮比较;第m轮比较(5-1-m)次一般地:n个元素需n-1轮,第m轮比较n-1-m次12课堂互动①:数组基础问题1inta[5]={1,2};则a[1]、a[4]的值分别是多少?问题2数组inta[5]的合法下标范围是什么?引用a[5]会发生什么?问题3如何修改冒泡排序程序,使数组按从大到小排列?参考答案答1:a[1]=2;a[4]=0(部分初始化时,未赋值元素默认为0)答2:合法下标0~4;a[5]越界——C语言不做越界检查,可能导致程序出错答3:将交换条件if(x[n]>x[n+1])改为if(x[n]<x[n+1])即可138.1.2二维数组的定义与初始化定义格式:类型说明符数组名[常量表达式1][常量表达式2];常量表达式1→行下标;常量表达式2→列下标。例:inta[3][4];共3×4=12个元素四种初始化方式①按行赋初值inta[2][3]={{1,2,3},{4,5,6}};②按行序连续列出inta[2][3]={1,2,3,4,5,6};③部分赋初值intb[3][4]={{1},{4,3},{2,1,2}};//未赋值为0④全部赋值时行数可省inta[][3]={1,2,3,4,5,6};//列数不可省方式③中b的存储:第一行{1,0,0,0},第二行{4,3,0,0},第三行{2,1,2,0}(图8-5)二维数组a[3][4]:按行存放(图8-4)列0列1列2列3a[0]行a[0][0]a[0][1]a[0][2]a[0][3]a[1]行a[1][0]a[1][1]a[1][2]a[1][3]a[2]行a[2][0]a[2][1]a[2][2]a[2][3]先存a[0]行,再存a[1]、a[2]行,每行元素依次存放引入场景:学习小组5人、每人3门课成绩——用一维数组处理很不方便,二维数组按“行(学生)×列(课程)”组织更自然。148.1.2二维数组的引用与遍历引用方式:数组名[下标][下标];下标须在定义范围内:行0~2、列0~3inta[3][4];//3行4列a[3][4]=3;//错误!越界a[3][4]超出下标范围(合法为a[0][0]~a[2][3]),赋值出错嵌套循环遍历外层for遍历行(i:0~2)内层for遍历列(j:0~3)内层循环结束后输出换行符例8-5二维数组的遍历#include<stdio.h>voidmain(){

//声明并初始化数组

intarray[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};

for(inti=0;i<3;i++)//遍历行{

for(intj=0;j<4;j++)//遍历列printf("[%d][%d]:%d",i,j,array[i][j]);printf("\n");}}▍运行结果[0][0]:1[0][1]:2[0][2]:3[0][3]:4[1][0]:5…[2][3]:12(共输出12个元素)15例8-6二维数组应用:小组成绩统计任务:用二维数组StuScore[5][3]存放5名同学3门课成绩(行=学生,列=课程),计算每人的总分及各门课的平均分。intStuScore[5][3]={{88,70,90},//张同学{80,80,60},//王同学{89,60,85},//李同学{80,75,78},//赵同学{70,80,80}};//周同学for(m=0;m<5;m++){//遍历每个学生nStuTotalScore=0;

for(n=0;n<3;n++)//累加三门课成绩nStuTotalScore+=StuScore[m][n];printf("个人总成绩:%d\n",nStuTotalScore);}//各科平均分=各科总分/5(switch统计各科总分)▍运行结果个人总成绩:张同学:248王同学:220李同学:234赵同学:233周同学:230数学平均分:81.40语文平均分:73.00英语平均分:78.60程序要点双层循环遍历成绩矩阵:外层选学生、内层累加成绩switch分支按列号(n)累加各科总分平均分计算注意整型转浮点:(double)总分/5168.1.3多维数组与本节小结多维数组定义格式:数组类型数组名[n1][n2]…[nn];示例:intx[3][4][5];x的每个元素又是一个二维数组,用法与二维数组相似,用于特定场合(如三维空间数据)。易错提醒数组下标从0开始,最大下标=长度-1部分初始化时未赋值元素默认为0二维数组全部赋初值时行数可省、列数不可省8.1数组·小结数组:相同数据类型变量的集合,元素按下标有序排列一维数组:定义/三种初始化/引用(下标法)常见操作:遍历、求最值、冒泡排序(n-1轮、每轮n-1-m次)二维数组:按行存放、四种初始化、嵌套循环遍历多维数组:维数=下标个数,用法与二维类似数组是后续学习字符串(字符数组)、指针、结构体数组的基础178.2函数与模块化设计第2学时8.2.1函数的定义、调用与声明·8.2.2函数的嵌套与递归调用188.2引例:为什么需要函数程序规模变大后的烦恼功能较多、规模较大时,全部代码写在一个main函数中:main函数复杂、臃肿,不便于阅读和维护同一功能(如打印表头)要多次重复编写,程序冗长繁琐“组装”思想如同组装计算机:事先生产好CPU、主板、硬盘等部件,用到什么就取什么。程序设计中的模块化思想——把常用功能用函数实现,需要时调用即可。函数(function):完成一定功能的代码块,每个函数即一个模块(module)。函数是C语言中模块化编程的最小单位。函数可单独设计、调试、测试——可自制,也可用现成的标准产品。图8-6C程序结构示意图main()f1()f2()f3()f4()一个C程序=一个主函数+若干其它函数由主函数调用其它函数,其它函数也可互相调用同一函数可被一个或多个函数多次调用生活原型:经理(main)把成本核算、市场分析等任务分派给员工(函数),布置工作时交付必要数据(参数),完成后提交报告(返回值)。19函数的分类函数生来平等,没有高低贵贱之分,只是main()稍微特殊一点。从使用角度可分两类:库函数(标准函数)printf()、scanf()等都是ANSIC标准定义的库函数,符合标准的编译器必须提供。使用方法:程序开头包含所在头文件例如使用数学库函数前加上#include<math.h>还有数量巨大的第三方函数库(图形、网络、数据库等),扩充C语言的功能,有的免费、有的需购买,使用时除包含头文件外还需额外配置。自定义函数自己定义的函数,解决特定问题。自定义函数包装后也可成为函数库,供别人(或自己以后)使用本章核心任务:学会自己“设计零部件”——定义函数、装配调用。208.2.1函数的定义一般形式:返回值类型函数名(类型参数1,类型参数2,……){声明语句序列;可执行语句序列;return表达式;}无返回值时用void函数名(void){……return;}要点说明函数名:函数的唯一标识,命名规则与变量相同函数体:必须用一对花括号包围,规则与main()完全一样变量必须“先定义,后使用”,函数也一样返回值类型与参数的说明详见后续页注意:上例并非一个可运行的程序——有main()的程序才能运行,函数必须被main()直接或间接调用才能发挥作用。例8-7求平均值的函数定义intaverage(intx,inty)//返回x和y的平均值{

intresult;result=(x+y)/2;

returnresult;}两个参数x、y;返回值与参数类型均为int;功能是返回两参数的平均值。21函数的调用、参数传递与返回值形参与实参形式参数(Parameter):函数内接收数据的参数,如x、y实际参数(Argument):调用者提供的参数,如a、b实参的数量必须与形参相等,类型必须匹配实参与形参有各自的存储空间,形参值的改变不会影响实参(值传递)返回值函数的返回值只能有一个,类型可以是除数组以外的任意类型;void表示没有返回值执行到return语句立即返回调用者,不再继续执行例8-8函数调用intaverage(intx,inty){intresult;result=(x+y)/2;

returnresult;}main(){inta=12,b=24,ave;…ave=average(a,b);//调用…}图8-7函数的调用过程(每次执行都建立全新的独立环境)①在栈中为main()的每个变量分配内存②转到average(),为形参x、y开辟新的栈空间③把实参a、b的值复制给形参x、y,执行函数内第一条语句④退出:求出返回值→收回所有变量内存→控制权交还调用者22函数原型:先声明,后使用两种写法写法一(例8-8):average()定义在前,main()在后,编译到main时已知函数信息写法二(例8-9):main()在前,average()在后——必须在main之前声明函数原型,否则编译器不知道average()的样子函数原型(FunctionPrototype)语法格式与函数定义的首部完全一致,只是末尾多一个分号。小知识stdio.h、math.h等头文件的内容,主要就是各库函数的原型声明,语法与功能与此处完全一致。可到编译器include目录下打开查看。例8-9函数原型的使用intaverage(intx,inty);//原型声明main(){

inta=12,b=24,ave;…ave=average(a,b);…}intaverage(intx,inty)//函数定义{

intresult;result=(x+y)/2;

returnresult;}两种写法功能上完全等价,区别仅在于average()与main()谁的定义在前。23函数的封装与设计原则封装(Encapsulation)调用者只需知道参数的类型、数量、顺序及返回值类型,不必了解函数的实现细节——如同开车不必懂发动机构造设计者可专注参数处理与功能实现,便于单独测试、排错和多人开发函数设计的基本原则函数规模要小函数功能要单一函数接口定义要清楚目标:“高内聚,低耦合”,方便被重用函数设计注意点(防御性编程)①入口参数有效性检查②敏感操作前的检查③调用成功与否的检查名言共勉:计算机科学的所有问题,都可以通过增加一个间接层来解决。248.2.2函数的嵌套调用什么是函数嵌套在调用一个函数的过程中,又调用另一个函数。C语言规定:函数不能嵌套定义,但可以嵌套调用——函数是相互平行的。生活中的类比经理把任务交给部门主管,主管再把子任务分给员工——层层委托,各司其职,最终结果逐级上报。要点main()调用a(),a()又调用b();b()执行完返回a(),a()执行完返回main()。图8-8函数的嵌套调用main()a()b()①调用a()②返回main()③调用b()④返回a()执行轨迹:main()→a()→b()→返回a()→返回main(),调用路径与返回路径严格对称。25递归调用:函数调用自己什么是递归递归调用是一种特殊的嵌套调用:函数直接或间接调用自己,将大工作逐渐分解为小工作。搬石头的比喻搬50块石头:先搬走49块,剩下的一块就能搬完;那49块同理——先搬走48块……问题逐层化简。递归程序必须包括两部分:1)递归继续的过程2)递归终止的过程(缺一不可,否则无限循环)if(递归终止条件成立)

return递归公式的初值;else

return递归函数调用返回的结果值;例8-10递归法计算n!longfact(intn){

if(n<0)

return-1;//非法输入

else

if(n==0||n==1)

return

1;//终止条件

else

returnn*fact(n-1);//递归}图8-9n!的化简:n!=n×(n-1)!=n×(n-1)×(n-2)!=……=n×(n-1)×…×2×1!(1!=1即终止条件)26递归的执行过程与优缺点图8-10fact(5)的递归调用过程fact(5)5*fact(4)fact(4)4*fact(3)fact(3)3*fact(2)fact(2)2*fact(1)fact(1)return1逐层返回下降阶段逐层化简,到达fact(1)满足终止条件;返回阶段逐层相乘,最终得到5!=120。递归的优点直观、精炼,逻辑清楚,编程简单符合思维习惯,接近数学公式表示适合非数值计算:汉诺塔、骑士游历、八皇后(回溯法)递归的缺点增加函数调用开销:参数传递、现场保护等耗费更多的时间和栈空间应尽量用迭代形式替代例8-11迭代法计算n!unsigned

longFact(unsigned

intn){unsigned

longresult=1;unsigned

inti;

for(i=1;i<=n;i++)result*=i;

returnresult;}27课堂互动②:函数与递归问题1在函数内修改形参x的值,调用处的实参a会随之改变吗?为什么?问题2递归函数若缺少终止条件,会发生什么?问题3用递归求5!时,fact()函数共被调用了几次?参考答案答1:不会。实参与形参有各自的存储空间,参数传递是“值复制”(值传递)。答2:递归无终止条件会无限调用自身,不断消耗栈空间,最终导致栈溢出、程序崩溃。答3:5次:fact(5)→fact(4)→fact(3)→fact(2)→fact(1),其中fact(1)满足终止条件返回1。288.2函数·小结知识框架函数分类:库函数/自定义函数函数定义:返回值类型函数名(形参表){……}函数调用:实参→形参(值传递,各自存储)函数原型:先声明后使用,与首部一致加分号返回值:只有一个;return立即返回调用机制嵌套调用:不能嵌套定义,可以嵌套调用递归调用:函数调用自己(直接/间接)递归两要素:继续过程+终止条件递归优点:直观精炼,接近数学公式递归缺点:调用开销大,尽量用迭代替代编程习惯:函数规模要小、功能要单一、接口要清楚;对入口参数、敏感操作、调用结果做检查(高内聚、低耦合)。下一节预告:函数的参数传递只能传“值”——如何让函数“改得到”外面的变量?答案是指针(8.3)。298.3指针第3学时指针的基本知识·指针与数组·指针与字符串·指针与函数·指针数组指针是C语言最重要的数据类型,也是最难的部分——除理解概念外,还须多编程、多上机调试308.3.1指针的基本知识:内存地址内存单元与地址存储器的一个字节称为一个内存单元;不同数据类型占用的单元数不同(如整型占2个单元、字符型占1个单元)。内存地址:为正确访问内存单元,给每个单元一个唯一的编号——通常也把内存地址称为指针。数据存入内存需记录两条信息:①分配空间的首地址②分配空间的大小两个关键区分单元的地址是指针;单元中存放的数据是内容——地址与内容是两回事。内存示意图(地址自低到高)2000变量i(整型)首地址20002002变量j(整型)首地址20022004ch(字符型)首地址2004,占1字节地址增大好比宾馆房间:房间号是地址(指针),住客是内容;凭房间号才能找到住客。31指针变量的定义与赋值指针变量变量的指针:变量的地址指针变量:存放变量地址的变量,用来指向另一个变量;程序中用“*”表示“指向”关系定义形式:类型说明符*变量名;*表示这是指针变量;类型说明符表示所指向变量的数据类型char*pc1;//pc1指向字符型变量的指针变量一个指针变量只能指向同类型的变量(pc1不能时而指向字符、时而指向整型)两种赋值方式(使用前必须赋具体值)/*①初始化的方法*/inta;/*定义整型变量a*/int*pi1=&a;/*定义并初始化为a的地址*//*②赋值语句的方法*/inta,*pi1;/*先定义*/pi1=&a;/*再把a的地址赋给pi1*/警告使用未经赋值的指针变量(野指针)将造成系统混乱,甚至死机!32与指针相关的两个运算符:&和*&取地址运算符*指针运算符(间接访问)inti=200,x;int*pi1;pi1=&i;//pi1指向ix=*pi1;//等价于x=i图8-11pi1与i的联系(设i的地址为1800)pi11800i200地址1800*pi1访问以pi1的值为地址的存储区域,即i所占区域运算规则与表达式&和*都是单目运算符,优先级相同,自右向左结合&*pi1先*后&→等价于&i(取i的地址)*&i先&后*→等价于*pi1,即变量i本身指针变量可出现在表达式中(设intx,y,*px=&x;)y=*px+5;//x的内容加5赋给yy=++*px;//++(*px):x先加1再赋yy=(*px)++;//y=x,然后x加1y=*px++;//相当于y=*px;px++//注意:(*pi1)++相当于i++;//而*pi1++则pi1不再指向i!338.3.2指针与数组:指向数组的指针数组指针的概念数组占用一块连续的存储单元;数组指针是数组的起始地址,数组元素的指针是数组元素的地址。让指针指向数组inta[10];/*含10个整型数据的数组*/int*p;p=&a[0];/*或p=a;两者等价*/C语言规定:数组名代表数组的首地址,即第0号元素的地址。因此p、a、&a[0]均指向同一单元;但p是变量,a和&a[0]是常量(a不能被赋值)。图8-12p指向a数组135791113151719a[0]a[9]p,a,&a[0]也可以在定义指针变量时赋初值:inta[10];int*p=&a[0];或int*p=a;数组名的本质:地址常量——这正是“指针与数组关系密切”的根源。34通过指针引用数组元素C语言规定:若p已指向数组中一个元素,则p+1指向同一数组中的下一个元素(不是简单加1个字节!)。图8-13p+i和a+i的指向a[0]a[1]a[2]……a[8]p,ap+1,a+1p+i,a+ip+8,a+8由此得到三条等价关系(设p=&a[0]):①p+i和a+i就是a[i]的地址(指向第i个元素)②*(p+i)或*(a+i)就是a[i](如*(p+8)即a[8])③指向数组的指针变量也可带下标:p[i]与*(p+i)等价引用数组元素的两种方法下标法:a[i]形式,直观常用指针法:*(a+i)或*(p+i)形式,间接访问;p是指针变量,a是数组名(初值p=a)/*下标法与指针法输出等价*/printf("%d",a[3]);printf("%d",*(a+3));printf("%d",*(p+3));//或p[3]35指针与多维数组:二维数组的地址表示以inta[3][4]为例:a是数组名,表示整个二维数组的首地址,也是第0行的首地址;a+1代表第1行的首地址。表示形式含义地址值a二维数组名,指向一维数组a[0],即第0行首地址6356720a[0],*(a+0),*a第0行第0列元素的首地址6356720a+1,&a[1]第1行首地址6356736a[1],*(a+1)第1行第0列元素的地址6356736a[1]+2,*(a+1)+2,&a[1][2]第1行第2列元素的地址6356744*(a[1]+2),*(*(a+1)+2),a[1][2]第1行第2列元素的值14记忆要诀:二维数组可看作“一维数组的数组”——a+i是第i行首地址,*(a+i)仍是地址(第i行第0列),*(*(a+i)+j)才是元素a[i][j]。表8-1二维数组元素的各种地址的表示形式(设a数组的首地址为6356720)368.3.3指针与字符串字符串的两种表示形式①用字符数组存放一个字符串②用字符指针指向一个字符串/*①字符数组方式*/charstring[]="IloveChina!";/*②字符指针方式*/char*string="IloveChina!";C语言将字符串常量按字符数组处理:在内存中开辟一个字符数组来存放字符串常量。应用:用指针变量指向格式字符串,可代替printf中的格式字符串。字符串指针与字符数组的区别①存放内容不同:字符串指针是变量,存放字符串的首地址;字符数组由若干元素组成,可存放整个字符串。②赋值方式不同:指针方式可在任意时候赋值;数组只能定义时整体赋初值,不能在赋值语句中整体赋值。char*ps="CLanguage";/*合法*/ps="CLanguage";/*合法*/charst[20];st={"CLanguage"};/*错误!*/数组只能对各个元素逐个赋值378.3.4指针与函数①:函数指针变量函数指针变量函数在内存中占用一段连续区域,函数名是该区域的首地址(入口地址)。可把函数入口地址赋给指针变量,使它指向该函数。定义形式:类型说明符(*指针变量名)();类型说明符表示被指函数的返回值类型;空括号表示指向的是函数三点注意赋值时只需给出函数名:pmax=max;不能写pmax=max(a,b)调用形式:(*指针变量名)(实参表),如z=(*pmax)(x,y);函数指针不能做数值运算:pmax+n、pmax++、pmax--毫无意义#include<stdio.h>intmax(inta,intb){if(a>b)returna;

else

returnb;}main(){

intmax(inta,intb);

int(*pmax)();//函数指针变量

intx,y,z,z1;pmax=max;//指向max函数scanf("%d%d",&x,&y);z=(*pmax)(x,y);//指针调用z1=max(x,y);//函数名调用printf("maxmum=%d",z);}38指针与函数②:指针型函数指针型函数函数的返回值可以是一个指针(地址),这种返回指针值的函数称为指针型函数。定义形式:类型说明符*函数名(形参表)函数名前加*表明返回值是指针;类型说明符是返回指针所指向的数据类型int*ap(intx,inty){……/*函数体*/}ap两侧的*与():()优先级高,ap先与()结合表明是函数,再与*结合表明返回指针。易混淆:函数指针vs指针型函数int(*p)();变量说明——p是指向函数入口的指针变量,该函数返回整型值int*p();函数说明——p是指针型函数,返回值是指向整型数据的指针;一般还应有函数体398.3.5指针数组与指针的指针指针数组:元素类型为指针的数组——所有元素都是指向相同数据类型的指针变量。char*pc[5];/*指针数组*/int*a[size];/*指针数组*/int(*a)[size];/*指向数组的指针*/[]优先级高于*:pc先与[5]结合成数组,每个元素都是指向char型数据的指针。辨析(常考):int*a[size]是指针数组;int(*a)[size]是数组指针(指向含n个元素的一维数组)。指针的指针:存放另一个指针变量地址的指针变量。定义:类型说明符**变量名图8-14指向指针的指针(name为指针数组)pname[0]“Followme”name[1]“BASIC”name[2]“GreatWall”name[3]“FORTRAN”p=name+2→*p即name[2],**p即字符串“GreatWall”char*name[]={"Followme","BASIC",

"GreatWall","FORTRAN","Computerdesign"};char**p;/*指向指针的指针*/for(i=0;i<5;i++){p=name+i;printf("%s\n",*p);}*p是p所指向的另一个指针变量;**p相当于*(*p),最终指向具体数据。40指针的数据类型与运算小结表8-2有关指针的数据类型的小结定义含义inti;定义整型变量iint*p;定义指向整型数据的指针变量pinta[n];定义整型数组a,它有n个元素int*p[n];定义指针数组p,由n个指向整型数据的指针元素组成int(*p)[n];p为指向含n个元素的一维数组的指针变量intf();f为返回整型值的函数int*p();p为返回一个指针的函数,该指针指向整型数据int(*p)();p为指向函数的指针,该函数返回一个整型值int**p;p是指针变量,它指向一个指向整型数据的指针变量指针的全部运算①加减整数:p++、p--、p+i、p±=i——原值加减n×单元字节数,非简单加减②赋值:p=&a;p=array;p=&array[i];p=max;p1=p2;(p=1000直接赋整数不合法)③空值:p=NULL,不指向任何变量④两指针相减:指向同一数组时,差值为两指针间元素个数⑤两指针比较:指向同一数组时可比较——指向前面的元素“小于”后面⑥void指针:不指定指向哪一种类型数据的指针41课堂互动③:指针问题1数组名a与指针变量p都可表示数组首地址,两者有何本质不同?问题2设p指向a[0],则*(p+3)与*p+3各是什么含义?问题3int*p[3];与int(*p)[3];分别定义了什么?参考答案答1:a是地址常量,不能被赋值、不能a++;p是变量,可p++移动指向各元素。答2:*(p+3)是元素a[3];*p+3是a[0]的值加3(*优先级高于+)。答3:int*p[3]是指针数组(3个指针元素);int(*p)[3]是数组指针(指向含3个元素的整型数组)。428.3指针·小结知识框架指针=地址;指针变量存放地址&取地址、*间接访问,自右向左结合定义:类型说明符*变量名(只能指向同类型)赋值:初始化/赋值语句;杜绝野指针数组名=首地址常量;p+i指向下一元素二维数组:a+i行首地址,*(*(a+i)+j)元素应用场景指针与一维数组:下标法/指针法访问元素指针与字符串:字符指针可整体赋值函数指针:(*p)()调用函数,不能做运算指针型函数:返回指针值*f()指针数组*p[n]/数组指针(*p)[n]指针的指针**p,指向指针数据学习建议:指针是C语言最困难的部分——除正确理解基本概念外,务必多编程、多上机调试(如打印p、*p、&a的值观察规律)。下一节预告:学生成绩表包含学号、姓名、性别、成绩等不同类型数据——数组无能为力,结构体登场(8.4)。438.4结构体和共用体第4学时·上8.4.1结构体·8.4.2共用体448.4.1结构体:为什么需要结构体问题引入学生成绩管理(表8-3)中,每组数据包含学号、姓名、性别、入学时间、各科成绩——学号、入学时间:整型或字符型姓名、性别:字符型成绩:整型或实型数组的尴尬:数组中各元素的类型必须一致,无法存放这组不同类型的数据。结构体(structure):C语言提供的一种构造数据类型,把不同类型的数据组合成一个有机整体。表8-3某校学生成绩管理表学号姓名性别入学时间程序设计英语高数1张珊珊女20209085982王思思女20208592833陈梧桐男2020788388一行=一名学生的完整信息:不同类型数据的组合对比:三种构造类型数组:同类型元素的集合结构体:不同类型成员的组合共用体:不同类型成员共用同一段内存(8.4.2)45结构体的定义与结构变量的说明/*结构定义的一般形式*/struct结构名{成员表列;/*类型说明符成员名;*/};structstudent{

intnum;/*学号*/

charname[20];/*姓名*/

charsex;/*性别*/

floatscore;/*成绩*/};结构须先定义后使用(如同函数先定义后调用)。说明结构变量的三种方法①先定义结构,再说明变量structstudent{……};structstudentstu1,stu2;②定义结构类型的同时说明变量structstudent{……}stu1,stu2;③直接说明(无名结构)struct{……}stu1,stu2;三种方法说明的stu1、stu2结构完全相同(图8-15)46结构变量成员的引用与赋值成员引用形式:结构变量名.成员名如stu1.num即stu1的num成员;stu2.sex即stu2的sex成员例:结构变量的赋值#include<stdio.h>main(){

struct{

intnum;

char*name;

charsex;

floatscore;}stu1,stu2;stu1.num=102;/*逐个成员赋值*/="Gaoping";printf("inputsexandscore\n");scanf("%c%f",&stu1.sex,&stu1.score);stu2=stu1;/*整体赋值!*/printf("%d%s%c%f",stu2.num,,stu2.sex,stu2.score);}赋值方式逐个成员赋值:stu1.num=102;输入语句赋值:scanf("%c%f",&stu1.sex,&stu1.score);同类型结构变量可整体赋值:stu2=stu1;注意不能将结构变量作为整体输入/输出,只能对其成员逐个操作;成员char*name可直接赋予字符串。47结构变量的初始化例:定义时初始化#include<stdio.h>main(){

struct{

intnum;

char*name;

charsex;

floatscore;}stu2,stu1={102,"Zhangping",

'F',98.5};/*初始化*/stu2=stu1;/*整体赋值*/printf("Number=%d\nName=%s\n",stu2.num,);printf("Sex=%c\nScore=%f\n",stu2.sex,stu2.score);}▍运行结果Number=102Name=ZhangpingSex=FScore=98.500000图8-15stu1、stu2变量的结构num(整型)name(指针)sex(字符)score(浮点)各成员按定义顺序依次排列,长度可不相同初始化要点定义时按成员顺序用{}列出初值初始化后再整体赋值:stu2=stu1结构体×数组structstudentstu[30];可定义结构体数组,管理全班学生——构造类型可组合使用。488.4.2共用体:几种数据共用一段内存共用体(union):使几个不同类型的变量共占同一段内存的结构,也称“联合体”。unionnumber/*类型声明*/{

shortx;

floaty;};定义变量的三种方法与结构体完全相同(先声明再定义/同时定义/无名直接定义)。内存大小:由占用字节数最大的成员决定(如unionnumber占4字节)。图8-16共用体成员共占同一段内存同一段内存(4字节)shortx(占2字节)floaty(占4字节)对x赋值会覆盖y的内容,反之亦然——某一时刻只有一个成员有效,不能同时对多个成员赋值。典型应用:如“教师-学生”表中,同一栏目不同身份存不同数据,用共用体最省内存。49共用体的特点·课堂互动④·8.4小结共用体类型数据的特点同一内存段可存放几种不同类型的成员,但某一时刻只有一个是有效的有效的成员是最后一次存入的成员共用体变量的地址与各成员的地址是同一地址不能对共用体变量名赋值,也不能在定义时初始化共用体变量不能作函数参数和返回值(可用指向共用体的指针)共用体可出现在结构体定义中,也可定义共用体数组课堂互动④①unionnumber{shortx;floaty;};的变量占几个字节?②结构体与共用体的根本区别是什么?参考答案答①:4字节(取最大成员float的长度)。答②:结构体成员各占独立内存(长度=各成员之和);共用体成员共用同一段内存(长度=最大成员)。8.4结构体和共用体·小结结构体:struct定义→三种说明变量方法→成员引用(变量.成员)→赋值/初始化(可整体赋值)共用体:union声明→共占内存→某一时刻仅一个成员有效→长度=最大成员508.5文件操作第4学时·下认识文件·打开和关闭·读写·检测函数程序运行结束,内存数据随之消失——文件让数据“永久保存”518.5.1认识文件:流与文件流(Stream)“逝者如斯夫”——数据像水一样流动。从数据流中连续读数,每次读到的都是新数据。文件流可以“倒流”:借助流控(FlowControl)命令可重读已读过的数据、定位到目标位置。文件:一组相关数据的有序集合,由文件名标识(源程序、目标、可执行、库、数据文件等)。从用户角度分类普通文件:磁盘上的有序数据集设备文件:显示器=标准输出,键盘=标准输入从编码方式分类(以数5678为例)ASCII文件(文本文件):每个字符对应一个字节,存放对应ASCII码001101015001101106001101117001110008共占4个字节,可按字符显示,能读懂(如源程序文件)二进制文件:按二进制的编码方式存放文件0001011000101110只占2个字节,内容无法直接读懂C系统不区分类型,都看成字符流,按字节处理(“流式文件”)52文件处理过程与文件指针图8-17文件处理过程打开文件建立文件相关信息,使文件指针指向该文件读/写文件在指针与文件关联后进行各种读写操作关闭文件断开指针与文件的联系,禁止再操作该文件文件指针FILE*fp;FILE为大写,是系统定义的结构类型,含文件名、状态、当前位置等信息;通过fp即可对所指文件实施操作。typedef

struct/*FILE结构示意*/{

shortlevel;/*缓冲区满/空程度*/

unsignedflags;/*文件状态标志*/

charfd;/*文件描述符*/

shortbsize;/*缓冲区大小*/

unsigned

char*buffer;/*缓冲区位置*/

unsigned

char*curp;/*当前读写指针*/……}FILE;/*编程时不必关心FILE的细节*/538.5.2文件的打开:fopen函数调用形式:FILE*fopen(filename,mode);filename为文件名(双引号字符串);mode为打开方式(表8-4)方式意义方式意义"rt"只读打开文本文件"rt+"读写打开文本文件"wt"只写打开/建立文本文件"wt+"读写打开/建立文本文件"at"追加打开文本文件"at+"读写/追加文本文件"rb"只读打开二进制文件"rb+"读写打开二进制文件"wb"只写打开/建立二进制文件"wb+"读写打开/建立二进制文件"ab"追加打开二进制文件"ab+"读写/追加二进制文件表8-4文件打开模式六字符记忆:r(read)读w(write)写a(append)追加t(text)文本可省b(binary)二进制+读写54打开注意事项与文件关闭打开文件的要点r方式文件必须已经存在,且只能读出w方式只能写入;文件不存在则新建,已存在则删去重建a方式向已存在文件末尾追加;文件不存在会出错打开出错fopen返回空指针NULL,可据此判断并处理文本vs二进制文本读写需ASCII↔二进制码转换,耗时较多标准文件键盘、显示器、出错输出由系统自动打开,可直接使用常用打开程序段if((fp=fopen("d:\\test","rb"))==NULL){printf("erroronopenfile!");getch();/*等待用户按键阅读*/exit(1);/*退出程序*/}返回NULL说明打开失败:提示→等待阅读→退出。文件关闭:fclosefclose(fp);正常关闭返回0,非0表示出错;用完即关,避免数据丢失。558.5.3字符读写:fgetc与fputc读字符fgetc字符变量=fgetc(文件指针);从指定文件读一个字符;文件须以读/读写方式打开每读一次,文件内部位置指针向后移动一个字节写字符fputcfputc(字符,文件指针);写入成功返回该字符,否则返回EOF两个“指针”要分清文件指针指向整个文件,不重新赋值则不变;文件内部位置指针指示当前读写位置,由系统自动移动。字符串读写:fgets(str,n,fp)读n-1个字符;fputs(st,fp)写字符串。例8-12读入文件test.txt并显示#include<stdio.h>main(){

FILE*fp;

charch;

if((fp=fopen("d:\\test.txt",

"r"))==NULL){printf("Cannotopenfile!");getch();exit(1);}ch=fgetc(fp);/*先读一个*/

while(ch!=EOF)/*遇结束符止*/{putchar(ch);/*显示*/ch=fgetc(fp);}/*再读下个*/fclose(fp);}56数据块读写:fread与fwrite整块数据的读写可一次读写一组数据——如一个数组元素、一个结构变量的值。fread(buffer,size,count,fp);fwrite(buffer,size,count,fp);参数含义buffer指针:存放输入(fread)/输出(fwrite)数据的首地址size数据块的字节数count要读写的数据块块数fp文件指针例8-16两名学生数据写入文件再读出#include<stdio.h>structstu{charname[10];intnum,age;

charaddr[15];}boya[2],boyb[2],*pp,*qq;main(){FILE*fp;inti;pp=boya;qq=boyb;

if((fp=fopen("stu_list","wb+"))==NULL){printf("Cannotopenfile!");exit(1);}

for(i=0

温馨提示

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

评论

0/150

提交评论