840 likes | 964 Vues
计算机实习. 第 1 讲. 教学任务. 作品 [ 申请成绩为优 ] 考核(结构体、文件、链表) 平时的实验 实习无补考 (2 次课堂教学 ,2 次实验 ,1 次考核 ). 3. 3. 复习数组. 1. 3. 文件的基本操作. 归纳与提高. 2. 4. 结构体数组. 目 录. 1 .数组的基本概念. 数组. #include "stdio.h" void f1(int a[],int n) { int i; for(i=0;i<n;i++) { printf(" 输入第 %d 个数据 <br>",i+1);
E N D
计算机实习 第1讲
教学任务 • 作品[申请成绩为优] • 考核(结构体、文件、链表) • 平时的实验 • 实习无补考(2次课堂教学,2次实验,1次考核)
3 3 复习数组 1 3 文件的基本操作 归纳与提高 2 4 结构体数组 目 录
1.数组的基本概念 数组 #include "stdio.h" void f1(int a[],int n) { int i; for(i=0;i<n;i++) { printf("输入第%d个数据\n",i+1); scanf("%d",&a[i]);}} main() { int x,i,s[50]; printf("输入数组的元素个数\n"); scanf("%d",&x); f1(s,x); for(i=0;i<x;i++) printf("%3d",s[i]); printf("\n");getch(); } int a[100]; 例如:
利用随机数为数组元素赋值 产生 n1—n2之间的随机整数: (int)((rand()/32768.0)*(n2-n1+1)+n1);
利用随机数为数组元素赋值 #include <stdio.h> #include"stdlib.h" #include "time.h" #define N 100 void f1(int a[],int n1,int n2,int n) { int i; srand( (unsigned)time( NULL ) ); for (i=0; i<n;i++) {a[i] =(int)((rand()/32768.0)*(n2-n1+1)+n1); printf("%4d,",a[i]); }}
利用随机数为数组元素赋值 main() { int a[N]; f1(a,40,150,20); }
利用数据文件为数组元素赋值 建立数据文件: 通过excel建立数据文件,文件以文本文件形式保存 用程序读出文本文件中的数据 定义文件指针 FILE *fp; 打开文件fopen("cj.txt","r") 读文件fscanf(fp,"%d",&x[i]); 关闭文件fclose(fp);
利用数据文件为数组元素赋值 #include <stdio.h> #define SIZE 143 main() { int x[SIZE],i,max,min; FILE *fp; printf("Enter %d integers:\n",SIZE); if((fp=fopen("cj.txt","r"))==NULL) { printf("不能打开该文件,任意键退出"); getch(); exit(1); }
利用数据文件为数组元素赋值 for(i=0;i<SIZE;i++) fscanf(fp,"%d",&x[i]); fclose(fp); max=min=x[0]; for(i=1;i<SIZE;i++) { if(max<x[i]) max=x[i]; if(min>x[i]) min=x[i]; } printf("Maximum is %d\n",max); printf("Minimum is %d\n",min); }
C文件概述 文件:存储在外部介质上数据的集合,是操作系统数据管理的单位
1文件分类 • 按文件的逻辑结构: • 记录文件:由具有一定结构的记录组成(定长和不定长) • 流式文件:由一个个字符(字节)数据顺序组成
按存储介质: • 普通文件:存储介质文件(磁盘、磁带等) • 设备文件:非存储介质(键盘、显示器、打印机等) • 按数据的组织形式: • 文本文件: ASCII文件,每个字节存放一个字符的ASCII码 • 二进制文件:数据按其在内存中的存储形式原样存放
如 int型数10000 ASCII形式 0011000100110000001100000011000000110000 0010011100010000 0010011100010000 内存存储形式 二进制形式 文件特点: 文本文件特点: 存储量大、速度慢、便于对字符操作 二进制文件特点: 存储量小、速度快、便于存放中间结果
缓冲文件系统: 程序 指令区 输入文件缓冲区 磁盘文件 用户数据区 磁盘 程序数据区 a 缓冲区 输出文件缓冲区 非缓冲文件系统: 文件处理方法 • 缓冲文件系统:高级文件系统,系统自动为正在使用的文件开辟内存缓冲区 • 非缓冲文件系统:低级文件系统,由用户在程序中为每个文件设定缓冲区
2文件类型指针 • 文件结构体FILE • 缓冲文件系统为每个正使用的文件在内存开辟文件信息区 • 文件信息用系统定义的名为FILE的结构体描述 • FILE定义在stdio.h中 typedef struct { int _fd; //文件号 int _cleft; //缓冲区中剩下的字符数 int _mode; //文件操作方式 char *_next; //文件当前读写位置 char *_buff; //文件缓冲区位置 }FILE;
文件名 文件使用 方式 操作系统 C程序 文件类型指针 磁盘 文件类型指针 • 指针变量说明: FILE *fp; • 用法: • 文件打开时,系统自动建立文件结构体,并把指向它的指针返回来,程序通过这个指针获得文件信息,访问文件 • 文件关闭后,它的文件结构体被释放
含义 文件使用方式 “r/rb” (只读) 为输入打开一个文本/二进制文件 “w/wb” (只写) 为输出打开或建立一个文本/二进制文件 “a/ab” (追加) 向文本/二进制文件尾追加数据 “r+/rb+” (读写) 为读/写打开一个文本/二进制文件 “w+/wb+” (读写) 为读/写建立一个文本/二进制文件 “a+/ab+” (读写) 为读/写打开或建立一个文本/二进制文件 3文件的打开与关闭 例 文件打开与测试 FILE *fp; fp=fopen(“aa.c”,“w”); if(fp==NULL) { printf(“File open error!\n”); exit(0); } • C文件操作用库函数实现,包含在stdio.h • 文件使用方式:打开文件-->文件读/写-->关闭文件 • 系统自动打开和关闭三个标准文件: • 标准输入------键盘 stdin • 标准输出------显示器 stdout • 标准出错输出-----显示器 stderr • 打开文件fopen • 函数原型: FILE *fopen(char *name,char *mode) 例 FILE *fp; char *filename=“c:\\fengyi\\bkc\\test.dat” fp= fopen(filename,”r”); 例 FILE *fp; fp= fopen (“c:\\fengyi\\bkc\\test.dat”,”r”); 使用文件方式 • 功能:按指定方式打开文件 • 返值:正常打开,为指向文件结构体的指针;打开失败,为NULL 要打开的文件名
缓冲文件系统: 输入文件缓冲区 磁盘文件 程序数据区 a fclose 输出文件缓冲区 文件关闭fclose • 作用:使文件指针变量与文件“脱钩”,释放文件结构体和文件指针 • 函数原型:int fclose(FILE *fp) • 功能:关闭fp指向的文件 • 返值:正常关闭为0;出错时,非0 文件打开时返回的文件类型指针 不关闭文件可能会丢失数据
4文件的读写 • 字符I/O:fputc与fgetc • fputc • 函数原型:int fputc(int c, FILE *fp) • 功能:把一字节代码c写入fp指向的文件中 • 返值:正常,返回c;出错,为EOF • fgetc • 函数原型:int fgetc(FILE *fp) • 功能:从fp指向的文件中读取一字节代码 • 返值:正常,返回读到的代码值;读到文件尾或出错,为EOF 文件I/O与终端I/O #define putc(ch,fp) fputc(ch,fp) #define getc(fp) fgetc(fp) #define putchar( c ) fputc(c,stdout) #define getchar( ) fgetc(stdin)
从键盘输入字符,逐个存到磁盘文件中,直到 输入‘#“为止 #include <stdio.h> main() { FILE *fp; char ch,*filename=“out.txt”; if((fp=fopen(filename,"w"))==NULL) { printf("cannot open file\n"); exit(0); } printf("Please input string:"); ch=getchar(); while(ch!='#') { fputc(ch,fp); putchar(ch); ch=getchar(); } fclose(fp); } 例 修改程序使之在文件out.txt中保存一句完整的C语句
编写程序,从键盘输入一个字符串,将其中的小写字母全部转换成大写字母,输出到磁盘文件“upper.txt”中保存。输入的字符串以“!”结束。同时将文件upper.txt中的内容显示在屏幕上。
数据块I/O: fread与fwrite size_t fread(void *buffer,size_t size, size_t count,FILE *fp) size_t fwrite(void *buffer,size_t size, size_t count,FILE *fp) • 函数原型: • 功能:读/写数据块 • 返值:成功,返回读/写的块数;出错或文件尾,返回0 • 说明: • typedef unsigned size_t; • buffer: 指向要输入/输出数据块的首地址的指针 • size: 每个要读/写的数据块的大小(字节数) • count: 要读/写的数据块的个数 • fp: 要读/写的文件指针 • fread与fwrite 一般用于二进制文件的输入/输出
例子 例 float f[2]; FILE *fp; fp=fopen(“aa.dat”,“rb”); fread(f,4,2,fp); for(i=0;i<2;i++) fread(&f[i],4,1,fp); 例 struct student { int num; char name[20]; char sex; int age; float score[3]; }stud[10]; for(i=0;i<10;i++) fread(&stud[i],sizeof(struct student),1,fp);
例 从键盘输入4个学生数据,把他们转存到磁盘文件中去 void save() { FILE *fp; int i; if((fp=fopen("d:\\fengyi\\exe\\stu_dat","wb"))==NULL) { printf("cannot open file\n"); return; } for(i=0;i<SIZE;i++) if(fwrite(&stud[i],sizeof(struct student_type),1,fp)!=1) printf("file write error\n"); fclose(fp); } void display() { FILE *fp; int i; if((fp=fopen("d:\\fengyi\\exe\\stu_dat","rb"))==NULL) { printf("cannot open file\n"); return; } for(i=0;i<SIZE;i++) { fread(&stud[i],sizeof(struct student_type),1,fp); printf("%-10s %4d %4d %-15s\n",stud[i].name, stud[i].num,stud[i].age,stud[i].addr); } fclose(fp); } #include <stdio.h> #define SIZE 4 struct student_type { char name[10]; int num; int age; char addr[15]; }stud[SIZE]; main() { int i; for(i=0;i<SIZE;i++) scanf("%s%d%d%s",stud[i].name,&stud[i].num, &stud[i].age,stud[i].addr); save(); display(); }
#include <stdio.h> main() { char s[80],c[80]; int a,b; FILE *fp; if((fp=fopen("test","w"))==NULL) { puts("can't open file"); exit() ; } fscanf(stdin,"%s%d",s,&a);/*read from keaboard*/ fprintf(fp,"%s %d",s,a);/*write to file*/ fclose(fp); if((fp=fopen("test","r"))==NULL) { puts("can't open file"); exit(); } fscanf(fp,"%s%d",c,&b);/*read from file*/ fprintf(stdout,"%s %d",c,b);/*print to screen*/ fclose(fp); } int fprintf(FILE *fp,const char *format[,argument,…]) int fscanf(FILE *fp,const char *format[,address,…]) • 格式化I/O:fprintf与fscanf • 函数原型: • 功能:按格式对文件进行I/O操作 • 返值:成功,返回I/O的个数;出错或文件尾,返回EOF 例 fprintf(fp,“%d,%6.2f”,i,t); //将i和t按%d,%6.2f格式输出到fp文件 fscanf(fp,“%d,%f”,&i,&t); //若文件中有3,4.5 ,则将3送入i, 4.5送入t 例 从键盘按格式输入数据存到磁盘文件中去
字符串I/O: fgets与fputs #include<stdio.h> main() { FILE *fp; char string[81]; if((fp=fopen("file.txt","w"))==NULL) { printf("cann't open file");exit(0); } while(strlen(gets(string))>0) { fputs(string,fp); fputs("\n",fp); } fclose(fp); if((fp=fopen("file.txt","r"))==NULL) { printf("cann't open file");exit(0); } while(fgets(string,81,fp)!=NULL) fputs(string,stdout); fclose(fp); } • 函数原型: char *fgets(char *s,int n,FILE *fp) int fputs(char *s,FILE *fp) • 功能:从fp指向的文件读/写一个字符串 • 返值: • fgets正常时返回读取字符串的首地址;出错或文件尾,返回NULL • fputs正常时返回写入的最后一个字符;出错为EOF fputs把s指向的字符串写入fp指向的文件 fgets从fp所指文件读n-1个字符送入s指向的内存区, 并在最后加一个‘\0’ (若读入n-1个字符前遇换行符或文件尾(EOF)即结束) 例 从键盘读入字符串存入文件,再从文件读回显示
5文件的定位 • 几个概念 • 文件位置指针-----指向当前读写位置的指针 • 读写方式 • 顺序读写:位置指针按字节位置顺序移动,叫~ • 随机读写:位置指针按需要移动到任意位置,叫~ • rewind函数 • 函数原型: void rewind(FILE *fp) • 功能:重置文件位置指针到文件开头 • 返值:无 #include <stdio.h> main() { FILE *fp1,*fp2; fp1=fopen("d:\\fengyi\\bkc\\ch12_4.c","r"); fp2=fopen("d:\\fengyi\\bkc\\ch12_41.c","w"); while(!feof(fp1)) putchar(fgetc(fp1)); rewind(fp1); while(!feof(fp1)) fputc(fgetc(fp1),fp2); fclose(fp1); fclose(fp2); } 例 对一个磁盘文件进行显示和复制两次操作
main() { int i; FILE *fp; if((fp=fopen("studat","rb"))==NULL) { printf("can't open file\n");exit(0); } for(i=0;i<3;i+=2) { fseek(fp,i*sizeof(struct student_type),0); fread(&stud[i],sizeof(struct student_type),1,fp); printf("%s %d %d %s\n", stud[i].name,stud[i].num,stud[i].age,stud[i].addr); } fclose(fp); } #include <stdio.h> struct student_type { int num; char name[10]; int age; char addr[15]; }stud[3]; fseek函数 • 函数原型: int fseek(FILE *fp,long offset,int whence) • 功能:改变文件位置指针的位置 • 返值:成功,返回0;失败,返回非0值 起始点 文件开始SEEK_SET 0 文件当前位置SEEK_CUR 1 文件末尾SEEK_END 2 文件指针 位移量(以起始点为基点,移动的字节数) >0向后移动 <0向前移动 • ftell函数 • 函数原型: long ftell(FILE *fp) • 功能:返回位置指针当前位置(用相对文件开头的位移量表示) • 返值:成功,返回当前位置指针位置;失败,返回-1L, 例 fseek(fp,100L,0); fseek(fp,50L,1); fseek(fp,-10L,2); 例 磁盘文件上有3个学生数据,要求读入第1,3学生数据并显示
6 出错的检测 • ferror函数 • 函数原型: int ferror(FILE *fp) • 功能:测试文件是否出现错误 • 返值:未出错,0;出错,非0 • 说明 • 每次调用文件输入输出函数,均产生一个新的ferror函数值,所以应及时测试 • fopen打开文件时,ferror函数初值自动置为0
clearerr函数 #include <stdio.h> int main(void) { FILE *stream; stream = fopen("DUMMY.FIL", "w"); getc(stream); if (ferror(stream)) { printf("Error reading from DUMMY.FIL\n"); clearerr(stream); } if(!ferror(stream)) printf("Error indicator cleared!"); fclose(stream); return 0; } • 函数原型: void clearerr(FILE *fp) • 功能:使文件错误标志置为0 • 返值:无 • 说明:出错后,错误标志一直保留,直到对同一文件调clearerr(fp)或rewind或任何其它一个输入输出函数 例 ferror()与clearerr()举例
proc1() {if((fp=fopen("book.txt","r"))==NULL) { printf("不能打开该文件,任意键退出"); getch(); exit(1); } }
int proc2(BOOK a[]) { int i=0; while (!feof(fp)) { fscanf(fp,"%s %s %s %d %d %d %f",a[i].bno,a[i].bname,a[i].cbadd,&a[i].bcdate.day,&a[i].bcdate.month,&a[i].bcdate.year,&a[i].bprice); i++; } return i;fclose(fp); }
将名单存在文本文件中,利用程序访问该文本文件后,输出学生名单。将名单存在文本文件中,利用程序访问该文本文件后,输出学生名单。
问题 与实际系统不同 图书的信息包括:书号、书名、出版日期、出版地点、价格 数据的独立性
3 3.6 在程序中存入变量 int x=3;float y=3.6; {10,20,30,40} int a[10]={10,20,30,40} {" zhang" , 27 , " li " , 18 , " wang " , 73} ; struct man { char name[20] ; int age ; }; struct man person [N] ={ " zhang" , 27 , " li " , 18 , " wang " , 73} ;
Name age sex address wang 20 F 10#222 Zhang 19 M 16#321 Liu 18 M 16#324 zhao 20 F 10#242 问题 例:处理50个学生的信息,将学号、姓名、年龄按成绩排序 • 解决方法: • 数组:相同类型数据的集合,将学号、姓名、年龄、成绩分别定义为不同的数组,相同的下标表示同一个学生的不同信息 • 结构体:不同类型数据的集合,将不同类型的数据作为结构体的分量,定义一个结构体数组 • 结构体:自定义数据类型
结构体 一、自定义类型声明 1、注意类型名与变量名的区别: 类型定义只规定在使用此结构体时应分配的内存, 对类型名本身并不分配内存,更不能赋值、存取、运算,定义结构体变量时才分配内存 结构体类型声明: struct结构体类型名 { 数据类型1 成员名1; …… 成员表列 数据类型n 成员名n;}; (1)学生信息结构体声明: struct student { int num; char name[20]; char sex; float score; }; 2、各“成员”也可称为“结构体分量”或“域”,用“;”分隔。成员数据类型可以是各种基本类型或构造类型(包括结构体类型),成员名可以与程序中的变量名相同。 3.声明时最后的右括号后要加分号
结构体 (2)为了处理通讯录,可以定义如下结构: struct address { char name[30]; /* 字符数组作为结构中的成员 */ char street[40]; /* 街道名称 */ char city[20]; /* 城市 */ char state[2]; /* 省市代码 */ unsigned long zip; /* 邮政编码,无符号长整型*/ }; struct person {char name[8]; /*姓名*/ char sex; /*性别*/ int age; /*年龄*/ float height , weight; /*身高、体重*/ char add[40]; /*住址*/ }; (3) 个人数据,包含姓名、性别、年龄、身高、体重、住址:
(5) 日期, 包括年、 月、 日: struct date {int year; /*年*/ int month; /*月*/ int day; /*日*/ }; 如考虑时间, 可作如下定义: struct time {int hh; /*时*/ int mm; /*分*/ int ss; /*秒*/ }; (4) 平面上的点: struct point2 {float x; /*横坐标*/ float y; /*纵坐标*/ }; 空间中的点: struct point3 {float x; /*X坐标*/ float y; /*Y坐标*/ float z; /*Z坐标*/ };
(6) 复数: struct complex { float re; /*实部*/ float ie; /*虚部*/ }; (7) 三角形: struct sjx { float a, b, c; /*三边*/ };
结构体 声明的一般形式: struct 结构体名 { 成员表列 }变量名列表; 二.变量定义 用已声明的类型定义变量 1、先声明后定义 struct student s1, s2; student:类型名称 s1, s2:变量名 2、声明类型同时定义 struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }s1, s2; 3、直接定义结构类型变量 struct {成员列表 }变量名列表; struct { int num; char name[20]; int age; float score; }s1, s2; 无类型名
Name age sex address wang 20 F 10#222 Zhang 19 M 16#321 Liu 18 M 16#324 zhao 20 F 10#242 结构体 表头 结构体成员(类型,名) 结构体变量 表中的内容
例如,对以上定义的结构体类型,我们可以定义如下一些结构体变量:例如,对以上定义的结构体类型,我们可以定义如下一些结构体变量: struct student zhang; /*张同学情况*/ struct person p[100]; /*100个人的数据*/ struct point2 p1, p2; /*平面上的两个点*/ struct point3 po1, po2; /*空间上的两个点*/
s1 num name age score 结构体 说明: (1)一旦定义了结构体变量后,则变量和其它变量一样,系统将分配其相应的存储空间. 存储空间:各个成员所占的存储单元的总和 (2)与标准类型变量不同:结构体变量的定义要有关键字struct,和结构体类型名。
结构体 3.为使用方便,可将对结构体类型的声明放入一文件中,在使用时用#include将其所在文件包入。还可定义一符号常量来代表一结构体类型。 #define STUDENT struct student STUDENT { int num; char name[20]; int age; float score; }; STUDENT s1, s2;
结构体 三、结构体类型变量的引用 • 引用格式:结构体变量名.成员名 “.”:成员运算符 如:s1.num=10001; 1.一般不将结构体作为一个整体进行操作,应对结构体变量中各成员分别实现各种操作和运算。由于结构体变量是一整体,要访问其中一个成员,必须先找到变量,通过成员运算符,从中找出某成员。 2.可嵌套定义。若成员本身又是结构体,则要用多个成员运算符一级级找到最低一级的成员,才能进行操作
结构体 例如: (1) struct student类型变量zhang, 其所有分量如下: zhang.no,zhang.name,zhang.sex,zhang.age, (2) struct point2类型变量p1, 其所有分量如下: p1.x, p1.y (3)嵌套的结构体类型和变量: struct data { int month; int day; int year; }; struct stud { int num; char name[20]; struct data birthday; }stud1; stud1.birthday.day=12;
结构体 3.对结构体变量可像普通变量一样进行各种运算,但应遵循各成员数据类型的运算规则。如: s1.age=s2.age; ++s1.age; 4.可将一结构体变量作为一个整体赋给另一同类型的结构体变量。不可将一组常量直接赋给结构体变量。如: s1=s2; s1={20102,“Li Hao”,20,10};
结构体 5.结构体变量的I/O同样要求对变量的各成员项来进行。 可引用结构体变量成员的地址,也可引用结构体变量的地址。如: scanf(“%d”,&s1.num); 6.成员名与变量名或其他变量同名,互不干扰:不同级,引用方式不同