首页
动态
归档
Github
留言
工具箱
更多
邻居
壁纸
音乐
Search
1
欢迎访问我的日志空间
8,244 阅读
2
C语言读写程序文件-学习三十二
742 阅读
3
WEB音乐播放器源码
712 阅读
4
Typecho - Joe主题魔改版(持续更新)
660 阅读
5
Typecho-1.1/17.10.30-定制版源码
639 阅读
学习笔记
源码库
BUG
第九艺术
登录
Search
标签搜索
学习笔记
C语言
typecho
Java
扫雷
源码
PHP
插件
网站源码
音乐播放器
模板
git
github
bug
XG.孤梦
累计撰写
50
篇文章
累计收到
44
条评论
首页
栏目
学习笔记
源码库
BUG
第九艺术
页面
动态
归档
Github
留言
工具箱
邻居
壁纸
音乐
搜索到
32
篇与
的结果
2022-02-27
C语言读写程序文件-学习三十二
顺序读写数据文件在顺序写时,先写入的数据存放在文件中前面,后写入的数据存放在文件中后面。在顺序读时,先读文件中前面的数据,后读文件中后面的数据。对顺序读写来说,对文件读写数据的顺序和数据文件中的物理顺序是一致的。读写一个字符的函数fgetc()函数原型:int fgetc( FILE * fp );调用形式:fgetc(fp)作用:从 fp 所指向的输入文件中读取一个字符。返回值:成功则返回的是读取的字符,发生错误则返回 EOF(即-1)。fputc()函数原型:int fputc( int c, FILE *fp );调用形式:fputc(c,fp)功能:把参数 c 的字符值写入到 fp 所指向的输出流(文件)中。返回值:写入成功,它会返回它会返回写入的字符(一个非负值),发生错误,则会返回 EOF(即-1)。例如:从一个磁盘文本文件顺序读入字符并在屏幕上显示出来。#include <stdio.h> #include <string.h> void main() { char c; FILE* fp = NULL; fopen_s(&fp, "a.txt", "r"); c = fgetc(fp); while (c != EOF) { putchar(c); c = fgetc(fp); } fclose(fp); }feof()函数调用方式:feof(fp)功能:对于二进制文件读取时判断是否结束。返回值:如果是文件结束,函数feof()值为1(真);否则为0(假)。例如:从一个磁盘二进制文件顺序读入字符并在屏幕上显示出来。#include <stdio.h> #include <string.h> void main() { char c; FILE* fp = NULL; fopen_s(&fp, "a", "r"); c = fgetc(fp); while (!feof(fp)) { putchar(c); c = fgetc(fp); } fclose(fp); }例子从键盘输入一些字符,逐个把它们送到磁盘上去直到用户输入一个“#”为止。#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) void main() { char c,filename[10]; FILE *fp = NULL; printf("请输入所用的文件名:"); scanf("%s", filename); if ((fp = fopen(filename, "w")) == NULL) { printf("无法打开此文件\n"); exit(0); } c = getchar(); // 接收执行scanf时最后输入的回车符 // 如果注释掉,文件中会首先换行,然后再输入的字符串 printf("请输入一个字符串(以#结束)"); c = getchar(); // 第一个输入的字符被赋给变量c while (c!='#') { fputc(c, fp); putchar(c); // 字符被输出到显示器 c = getchar(); } putchar(10); // 向屏幕输出一个换行符 fclose(fp); }将一个磁盘文件中的信息复制到另一个磁盘文件中。今要求建立的a.txt文件中的内容复制到另一个磁盘文件b.txt中。#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) void main() { char c=NULL,infile[10],outfile[10]; FILE *in = NULL; FILE *out = NULL; printf("请输入读取的文件名:"); scanf("%s", infile); printf("请输入输出的文件名:"); scanf("%s", outfile); if ((in = fopen(infile, "r")) == NULL) { printf("无法打开此文件\n"); exit(0); } if ((out = fopen(outfile, "w")) == NULL) { printf("无法打开此文件\n"); exit(0); } while (c!=EOF) { c= fgetc(in); fputc(c, out); putchar(c); // 字符被输出到显示器 } putchar(10); // 向屏幕输出一个换行符 fclose(in); fclose(out); }读写一个字符串的函数fgets()函数原型:char *fgets( char *buf, int n, FILE *fp );调用格式:char *fgets( str, n, fp ); 功能:从 fp 所指向的输入流(文件)中读取长度为(n - 1)的字符串存放到字符数组str中,并在最后追加一个 null 字符(即'\0')来终止字符串。返回值:读成功返回地址str ,失败则返回NULLfputs()函数原型:int fputs( const char *s, FILE *fp );调用格式:int fputs( str, fp );功能:str所指向的字符串写到文件指针变量fp所指向的文件中。返回值:如果写入成功,则返回写出的字符个数(一个非负值),如果发生错误,则返回EOF(即-1)。说明:fgets(str,n,fp);中 n 是要求得到的字符个数,但实际上只读 n-1 个字符,然后在最后加一个\0字符, 这样得到的字符串共有n个字符,把它们放到字符数组str中。fgets()函数,如果在读完 n-1 个字符之前就遇到一个换行符 \n 或文件的末尾 EOF, 则读入结束,则只会返回读取到的字符,包括换行符。fputs函数中第一个参数可以是字符串常量、字符数组名或字符型指针。fputs()函数字符串末尾的\0不输出。例子从键盘读入若千个字符串,然后把字符串送到磁盘文件中保存。#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) void main() { char str[3][10]; int i; FILE *fp = NULL; printf("请输入要写入的字符串:\n"); for (i = 0; i < 3; i++) { gets(str[i]); } if ((fp = fopen("a.txt", "w")) == NULL) { printf("无法打开此文件\n"); exit(0); } for (i = 0; i < 3; i++) { fputs(str[i], fp); fputs("\n", fp); printf("写入成功:"); printf("%s\n", str[i]); } fclose(fp); }读取文件中的字符串,打印到控制台#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) void main() { char str[3][10]; int i; FILE *fp = NULL; if ((fp = fopen("a.txt", "r")) == NULL) { printf("无法打开此文件\n"); exit(0); } for (i = 0; i < 3; i++) { fgets(str[i],10,fp); printf("%s", str[i]); } fclose(fp); }用格式化的方式读写文件一般调用方式为:fprintf(文件指针, 格式字符串, 输出表列);例如:fprintf(fp,"%d,%f",i,f);fscanf(文件指针, 格式字符串, 输入表列);例如:fscanf(fp,"%d,%f",&i,&f);用fprintf和fscanf对磁盘文件读写,使用方便,容易理解,但是由于在输入时要将ASCII码转换为二进制形式,在输出时又要将二进制形式转换符,花费时间比较多。因此在内存与磁盘频繁交换的情况下,最好不要用 fprintf 和 fscanf 函数,可以用fread 和 fwrite 函数。二进制 I/O 函数fread函数原型:size_t fread(void *buffer, size_t size, size_t count, FILE *a_file);返回值:如果读取成功,则返回读的块数,失败,则返回0 。fwrite函数原型:size_t fwrite(const void *buffer, size_t size, size_t count, FILE *a_file);返回值:如果写入成功,则返回写的块数,失败,则返回0 。这两个函数都是用于存储块的读写 - 通常是数组或结构体。例子:从键盘输入5个学生的有关数据,然后把它们转存到磁盘文件上去,读取并打印到控制台。#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) #define SIZE 5 struct Student { int num; char name[10]; int age; char addr[15]; }stu[SIZE]; void main() { void save(); void reads(); int i; printf("请输入学生数据:\n"); for (i = 0; i < SIZE; i++) { scanf("%d %s %d %s", &stu[i].num, stu[i].name, &stu[i].age, stu[i].addr); } save(); printf("学生信息:\n"); reads(); } void save() { int i; FILE *fp = NULL; if ((fp = fopen("stu.dat", "wb")) == NULL) { printf("无法打开此文件\n"); return; } for (i = 0; i < SIZE; i++) { if (fwrite(&stu[i], sizeof(struct Student), 1, fp) != 1) { printf("写入文件错误\n"); } } fclose(fp); } void reads() { int i; FILE *fp = NULL; if ((fp = fopen("stu.dat", "rb")) == NULL) { printf("无法打开此文件\n"); return; } for (i = 0; i < SIZE; i++) { fread(&stu[i], sizeof(struct Student), 1, fp); printf("%4d %-10s %4d %-15s\n", stu[i].num, stu[i].name, stu[i].age, stu[i].addr); } fclose(fp); }从已有的二进制文件“stu2.dat”中,读入数据并输出到“stu.dat”文件中#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) #define SIZE 7 struct Student { int num; char name[10]; int age; char addr[15]; }stu[SIZE]; void main() { void load(); void save(); void reads(); load(); save(); printf("学生信息:\n"); reads(); } void save() { int i; FILE *fp = NULL; if ((fp = fopen("stu.dat", "wb")) == NULL) { printf("无法打开此文件\n"); return; } for (i = 0; i < SIZE; i++) { if (fwrite(&stu[i], sizeof(struct Student), 1, fp) != 1) { printf("写入文件错误\n"); } } fclose(fp); } void reads() { int i; FILE *fp = NULL; if ((fp = fopen("stu.dat", "rb")) == NULL) { printf("无法打开此文件\n"); return; } for (i = 0; i < SIZE; i++) { fread(&stu[i], sizeof(struct Student), 1, fp); printf("%4d %-10s %4d %-15s\n", stu[i].num, stu[i].name, stu[i].age, stu[i].addr); } fclose(fp); } void load() { int i; FILE *fp = NULL; if ((fp = fopen("stu2.dat", "rb")) == NULL) { printf("无法打开此文件\n"); return; } for (i = 0; i < SIZE; i++) { if (fread(&stu[i], sizeof(struct Student), 1, fp) != 1) { if (feof(fp)) { fclose(fp); return; } printf("无法打开此文件\n"); } } fclose(fp); }其它读写函数putw 和 getw 函数作用:以二进制形式,对磁盘文件读写一个int型的整数,2个字节。返回值:成功,则返回所写的整数值;失败,则返回EOF例如:putw(10, fp); i=getw(fp);随机读写数据文件对文件进行顺序读写比较容易理解,也容易操作,但有时效率不高。文件位置标记为了对读写进行控制,系统为每个文件设置了一个文件读写位置标记(简称文件标记),用来指示“接下来要读写的下一个字符的位置”。一般情况下,在对字符文件进行顺序读写时,文件标记指向文件开头,进行读的操作时,就读第一个字符,然后文件标记向后移动一个位置,在下一次读操作时,将位置标记指向第二个字符读入,以此类推直到遇文件尾结束。随机读写可以在任何位置读取和写入数据文件位置标记的定位将文件的指针指向文件的开头,进行文件操作rewind函数函数原型:void rewind(FILE *fp);功能:重置文件位置指针到文件开头。返回值:无-例如:#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) void main(){ FILE *fp1, *fp2; fp1 = fopen("file1.dat", "r"); fp2 = fopen("file2.dat", "w"); while (!feof(fp1)) { putchar(getc(fp1)); } putchar(10); rewind(fp1); while (!feof(fp1)) { putc(getc(fp1), fp2); } fclose(fp1); fclose(fp2); }可以强制使文件标记指向指定的位置fseek函数调用形式:fseek(文件类型指针,位移量,起始点)起始点:0代表“文件开始位置(SEEK_SET)”,1为“文件当前位置(SEEK_CUR)”,2代表“文件的末尾(SEEK_END)”。位移量指以起始点为基点,向前移动的字节数。位移量应是long型数据(在数字的末尾加一个字母L)。fseek函数一般用于二进制文件。例如: fseek(fp,100L,0); // 将位置指针移动到离文件头100个字节处 fseek(fp,50L,1); // 离当前位置50个字节处 fseek(fp,-10L,2); // 从文件末尾处向后退10个字节 ftell函数由于文件中的文件位置标记经常移动,人们往往不容易知道其当前位置,所以常用ftell函数得到当前位置(相对于文件开头的位移量来表示)。如果调用函数时出错(如不存在fp指向的文件),ftell函数返回值为-1L。例如:i=ftell(fp);if(i==-1L){printf("error\n");}例子在磁盘文件上存有10个学生的数据要求将第1,3,5,7,9个学生数据输入计算机,并在控制台显示出来。#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) struct Student { int num; char name[10]; int age; }stu[10]; void main() { FILE *fp; int i; if ((fp = fopen("stu.dat", "rb")) == NULL) { printf("打开文件失败"); exit(0); } for (i = 0; i < 10; i+=2) { fseek(fp, i * sizeof(struct Student), 0); fread(&stu[i], sizeof(struct Student), 1, fp); printf("%4d %-10s %4d\n", stu[i].num, stu[i].name, stu[i].age); } fclose(fp); }文件读写的出错检测ferror函数一般调用形式:ferror(fp);返回值:如果为0,表示未出错,否则表示出错。每次调用输入输出函数,都产生新的ferror函数值,因此调用输入输出函数后立即检查。调用fopen时,ferror的初始值自动置为0。clearerr函数作用是使文件错误标志和文件结束标志置为0。调用一个输入输出函数时出现错误(ferror值为非零值),立即调用clearerr(fp),使ferror(fp)值变0,以便再进行下一次检测只要出现文件读写错误标志,它就一直保留,直到对同一文件调用 clearerr 函数或 rewind 函数,或任何其他一个输入输出函数总结在使用文件时,首先要定义一个文件指针:FILE *fp;然后通过该指针来操作相应的文件;通过fopen这个函数,使文件指针fp和相应的文件建立了联系,通过fclose函数将切断fp和文件的联系;如果以一次一个字符的方式处理文件,需要用 fgetc 或者 fputc 函数;如果以一次一行的方式处理文件,可以用 fgets 或者 fputs 函数;如果以一次一个结构体的方式处理文件,可以用 fread 和 fwrite 函数(多为二进制文件);
2022年02月27日
742 阅读
0 评论
0 点赞
2022-02-26
C语言文件-学习三十一
为什么要引入文件数据量大数据访问频繁程序结果需要长期保存使用文件的目的1.程序与数据分离:数据文件的改动不引起程序的改动;2.数据共享:不同程序可以访问同一数据文件中的数据;3.能长期保存程序运行的中间数据或结果数据。文件“文件”指存储在外部介质上数据的集合一批数据是以文件的形式存放在外部介质上的操作系统是以文件为单位对数据进行管理想找存放在外部介质上的数据,先按文件名找到指定的文件,然后再从该文件读数据要向外部介质上存储数据也必须先建立一个文件(以文件名作为标志),才能向它输出数据输入输出是数据传送的过程,数据如流水一样从一处流向另一处,因此常将输入输出形象地称为流(stream),即数据流。流表示了信息从源到目的端的流动。输入操作时,数据从文件流向计算机内存输出操作时,数据从计算机流向文件从C程序的观点来看,无论程序一次读写一个字符,或一行文字,或一个指定的数据区,作为输入输出的各种文件或设备都是统一以逻辑数据流的方式出现的。C语言言把文件看作是一个字符(或字节)的序列。输出流就是一个字符流或字节(内容为二进制数据)流。C的数据文件由一连串的字符(或字节)组成,而不考虑行的界限,两行数据间不会自动加分隔符,对文件的存取是以字符(字节)为单位的。输入输出数据流的开始和结束仅受程序控制而不受物理符号(如回车、换行符)控制,这就增加了处理灵活性。这种文件称为流式文件。文件名文件要有一个唯一的文件标识,以便用户识别和引用。文件标识包括三部分:1.文件路径2.文件名主干3.文件后缀文件路径表示文件在外部存储设备中的位置。例如: C:\VS\temp\file1.datC:\VS\temp\ 文件路径file1 文件名主干 (命名规则遵循标识符的命名规则).dat 文件后缀 (doc、txt、dat、c、cpp、obj、exeopt、 bmp等)文件的分类按存储介质:普通文件:存储介质文件(磁盘、磁带等设备文件)设备文件:非存储介质(键盘、显示器等设备文件)按数据的组织形式:文本文件:ASCII文件,每个字节存放一个字符的ASCII码文本文件特点:存储量大、速度慢、便于对字符操作二进制文件:数据按其在内存中的存储形式原样存放文本文件特点:存储量小、速度快、便于存放中间结果例如:int型数 10000内存存储形式: 0010011 00010000存储以文本文件形式:1 0 0 0 0分别以 1 和 0 的 ASCII码 的形式与下面对应0110001 00110000 00110000 00110000 00110000存储以二进制文件形式: 0010011 00010000 (与内存存储形式一致)文件缓冲区ANSI C标准采用“缓冲文件系统”处理数据文件缓冲文件系统是指系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲。从内存向磁盘输出数据,必须先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘去文件类型指针每个被使用的文件都在内存中开辟一个相应的文件信息区,用来存放文件的有关信息(如文件的名字、文件状态及文件当前位置等)。这些信息是保存在一个结构体变量中的,该结构体类型是由系统声明的,取名为FILE。例如:在stdio.h头文件中有以下的文件类型声明:typedef struct { short level; // 缓冲区"满"或"空"的程度 unsigned flags; //文件状态标志 char fd; // 文件描述符 unsigned char hold; // 产如果没有缓冲区,则不读取字符 short bsize; // 缓冲区大小 unsigned char *buffer; // 产数据缓冲区的位置 unsigned char *curp; // 产当前激活指针 unsigned istemp; //产临时文件,指示器 short token; // 用于有效性检查 }FILE;用FILE定义文件指针例如:FILE *fp文件打开时,系统自动建立文件结构体,并把指向它的指针返回来,程序通过这个指针获得文件信息,访问文件。文件关闭后,它的文件结构体被释放。打开和关闭文件C文件操作用库函数实现,包含在stdio.h文件使用方式:打开文件 --> 文件读/写 --> 关闭文件系统自动打开和关闭三个标准文件:标准输人-----键盘 stdin标准输出-----显示器 stdout标准出错输出-----显示器 stderr用fopen函数打开数据文件“打开”是指为文件建立相应的信息区(用来存放有关文件的信息)和文件缓冲区(用来暂时存放输入输出的数据)。在编写程序时,在打开文件的同时,一般都指定一个指针变量指向该文件,也就是建立起指针变量与文件之间的联系,这样就可以通过该指针变量对文件进行读写。函数原型:FILE *fopen(char *name , char *mode);char *name 要打开文件的名字char *mode 使用文件的方式(读、写、读写)返回值:正常打开,返回指向文件结构体的指针;打开失败,返回NULL。fopen函数参数中的文件名可以带文件路径,但是文件路径中的“\”必须使用转义字符。例如:要打开在路径为C:\VS\temp\file1.dat的文件应该写成:fopen("C:\\VS\\temp\\file1.dat","r");mode 的值可以是下列值中的一个模式描述r打开一个已有的文本文件,允许读取文件。w打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。a打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。r+打开一个文本文件,允许读写文件。w+打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。a+打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:"rb" , "wb", "ab", "rb+" 或 "r+b", "wb+" 或 "w+b", "ab+" 或 "a+b"说明:1.用“r”方式打开一个文本文件,只能用于向计算机输入,而不能用作向该文件输出数据, 而且该文件应该已经存在,并存有数据,这样程序才能从文件中读数据。不能用“r”方式打开一个并不存在的文件,否则出错。为输入打开一个二进制文件,用“rb”。2.用“w”方式打开一个文本文件,只能用于向该文件写数据(即输出文件),而不能用来向计算机输入。如果原来不存在该文件,则在打开文件前新建一个以指定的名字命名的文件。如果原来已存在一个以该文件名命名的文件,则在打开文件前先将该文件删去,然后重新建立一个文件。为输出打开一个二进制文件,用“wb”。3.如果希望向文件末尾添加新的数据(不希望删除原有数据),则应该用“a”方式打开。但此时应保证该文件已存在;否则将得出错的信息。打开文件时,文件读写标记移到文件末尾。向二进制文件尾添加数据,用“ab”。4.用 “r+、w+、a+ ” 方式打开的文本文件既可以用来输入数据,也可以用来输出数据。如果是二进制文件用 “rb+、wb+、ab+ ”。5.在向计算机输入文本文件时,会将回车换行符转换为一个换行符,在输出时把换行符转换成为回车和换行两个字符。 在用二进制文件时,不进行这种转换,在内存中的数据形式与输出到外部文件中的数据形式完全一致,一一对应。6.如果打开失败,fopen函数将会带回一个出错信息。 fopen函数将带回一个空指针值NULL常用下面的方法打开一个文件: if ((fp = fopen("file1", "r")) == NULL) { printf("Can not open this file\n"); exit(0); // 终止正在执行的程序 }注意在VS中编译器会出现使用fopen()不安全,建议使用fopen_s()的提示格式:fp=fopen("文件路径","模式"); fopen_s(&fp,"文件路劲","模式");C标准建议用表列出的文件使用方式打开文本文件或二进制文件,但目前使用的一些C编译系统可能不完全提供所有这些功能。比如:有的C版本只能用 “r",“w",“a" 方式,有的C版本不用 “r+”,“w+”,“a+",而用 “rw”,“wr",“ar” 等。用fclose函数关闭数据文件“关闭”是指撤销文件信息区和文件缓冲区。关闭文件用fclose函数。fclose函数调用的一般形式为:fclose(文件指针);例如:fclose (fp);
2022年02月26日
624 阅读
0 评论
0 点赞
2022-02-25
C语言共用体与枚举类型-学习三十
共用体类型共用体也是一种构造类型,将不同类型的数据组合在一起。但与结构体类型不同,在共用体内的不同成员占用同一段存储区,成员之间互相覆盖,即在同一时刻只有一个成员起作用。为共用体变量分配空间的大小是以所有成员中占用空间字节数最多的成员为标准。共用体类型的声明及变量的定义共用体类型的声明与结构体的声明完全相同,只是关键字为union。共用体类型的声明格式:union 共用体名{ 数据类型 成员名1; 数据类型 成员名2; ... 数据类型 成员名n; };例如: union data { int i; char c; float f; };共用体变量的定义方式与结构体变量的定义方式相似,也有3种方式:1.类型声明与变量定义分开 union data { int i; char c; float f; }; union data d1, d2;2.在声明类型的同时定义变量 union data { int i; char c; float f; }d1, d2;3.直接定义共用体类型的变量,不给出共用体名 union { int i; char c; float f; }d1, d2;共用体变量初始化对于共用体变量,在定义的同时,可以初始化,但是初始化只能对第一个成员初始化。例如:union data d1={10}; 即 共用体里的int i = 10;共用体变量中成员的引用与结构体变量成员引用的方式相同,也使用 “->” 和 “.” 两种运算符来实现;格式:共用体变量名.成员名共用体指针->成员名例如:union data d1, *tpd; pd = &d1;对d1成员的引用可以是:d1.i 或 pd ->i 、d1.c 或 pd ->c、d1.f 或 pd ->f同类型的共用体变量之间可以互相赋值。例子:#include <stdio.h> union data{ int i; char c; float f; }; struct data2{ int i; char c; float f; }; void main() { union data ud; struct data2 sd; ud.i = 10; ud.c = 'A'; ud.f = 20; sd.i = 10; sd.c = 'A'; sd.f = 20; printf("size of ud: %d, size of sd: %d\n", sizeof ud, sizeof sd); printf("ud.i: %d,ud.c: %c,ud.f: %f\n", ud.i, ud.c, ud.f); printf("sd.i: %d,sd.c: %c,sd.f: %f\n", sd.i, sd.c, sd.f); }从上面的运行结果可看出对共用体变量成员的赋值,保存的是最后的赋值前面对其他成员的赋值均被覆盖。由于结构体变量的每个成员拥有不同的内存单元,因而不会出现这种情况。共用体与结构体比较1.结构体类型是一种复杂而灵活的构造数据类型,它可以将多个相互关联但类型不同的数据项作为一个整体进行处理。 定义结构体变量时,每一个成员都要分配空间存放各自的数据。2.共用体是另一种均造数据类型,定义共用体变量时,只按占用空间最大的成员来分配空间,在同一时刻只能存放一个成员的值。3.共用体变量的定义形式与结构体变量的定义形式类似,都有三种形式,只是关键字不同。4.结构体变量通过初值列表实现对变量中的成员初始化。共用体变量只能对第一个成员初始化。5.共用体成员的访问方式与结构体相同,成员的访问通过运算符 “.” 和 “->” 实现。例子设有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码、性别、职业、班级。教师的数据包括:姓名、号码、性别、职业、职务。现要求把它们放在同一表格中#include <stdio.h> #pragma warning (disable:4996) struct{ int num; char name[10]; char sex; char job; union { int banji; char position[10]; }category; }person[2]; void main() { int i; for (i = 0; i < 2; i++) { printf("请输入信息:\n"); scanf("%d %s %c %c", &person[i].num, &person[i].name, &person[i].sex, &person[i].job); if (person[i].job == 's') { scanf("%d", &person[i].category.banji); } else if(person[i].job=='t'){ scanf("%s", &person[i].category.position); } else { printf("输入错误!"); } } printf("\n"); for (i = 0; i < 2; i++) { if (person[i].job == 's') { printf("%-6d %-10s %-3c %-3c %-6d\n", person[i].num, person[i].name, person[i].sex, person[i].job, person[i].category.banji); } else { printf("%-6d %-10s %-3c %-3c %-6s\n", person[i].num, person[i].name, person[i].sex, person[i].job, person[i].category.position); } } }枚举类型如果一个变量只有几种可能的值,则可以定义为枚举(enumration)类型,所谓“枚举”就是把可能的值一一列举出来,变量的取值限于列举出来的值的范围内。枚举类型声明格式:enum 枚举类型名{ 枚举元素列表 // 枚举元素之间以逗号间隔 };枚举变量的定义:1.先声明枚举数据类型,再定义枚举变量 enum week{ sun , mon ,tue , wed , thu , fri , sat }; enum week workday;2.声明枚举类型的同时定义枚举变量 enum week{ sun , mon ,tue , wed , thu , fri , sat }workday;3.不指定枚举类型名,直接定义枚举变量 enum { sun , mon ,tue , wed , thu , fri , sat }workday;枚举变量workday的值只能在枚举元素sun,mon, .… sat之间取其中一个,不能超出这个范围。枚举类型使用时需注意以下几点:1.枚举元素是常量,有固定的值,不能将其作为变量使用例如: sun=0; mon = 1 错误,不能为枚举元素赋值2.每一个枚举元素都代表了一个整数,编译程序按定义时的顺序默认它们的值为0、1、2、3...... 上面的定义中,sun的值为0、mon的值为1、.....sat的值为6。例如:如果有赋值语句:workday = mon;相当于:workday = (enum week)1;一个整数不能直接赋给一个枚举变量先进行强制类型转换才能赋值。3.如果不希望使用默认的值,也可以在声明数据类型时指定值。例如指定枚举元素sun的值为7,mon的值为1,tue的值为2,以后顺序加1,sat为6。 enum { sun=7, mon=1, tue=2, wed , thu , fri , sat }workday;4.枚举值,可以用来作判断比较例如:if(workday == mon)5.不能有两个相同名字的枚举元素,枚举元素也不能与符号常量和变量同名。用typedef声明新类型名1.简单地用一个新的类型名代替原有的类型名typedef int Integer; typedef float Real; int i,j; float a,b; // 等价 Integer i,j; Real a,b;2.命名一个简单的类型名代替复杂的类型表示方法命名一个新的类型名代表结构体类型:typedef struct{ int month; int day; int year; }Date; // 声明新类型名Date,它代表上面指定的一个结构体类型 Date birthday; Date *p; // p为指向此结构体类型数据的指针命名一个新的类型名代表数组类型 typedef int Num[100]; //声明NUM为整型数组类型 Num a; //定义a为整型数组变量命名一个新的类型名代表一个指针类型 typedef char *String; // 声明String为字符指针类型 String p,s[10]; // p为字符指针变量,s为指针数组命名一个新的类型名代表指向函数的指针类型typedef int (*Pointer)(); // 声明Pointer为指向函数的指针类型,该函数返回整型值 Pointer p1,p2; // p1,p2为Pointer类型的指针变量声明一个新的类型名的方法:1.先按定义变量的方法写出定义体 int i;2.将变量名换成新类型名(将i换成Count)3.在最前面加 typedef4.得到 typedef int Count;以定义上述的数组类型为例来说明:1.先按定义数组变量形式书写:int a[100];2.将变量名a换成自己命名的类型名:int Num[100];3.在前面加上typedef,得到typedef int Num[100];4.用来定义变量:Num a;5.相当于定义了:int a[100];对字符指针类型,也是:char *p;char *String;typedef char *String;String p;说明:1.以上的方法实际上是为特定的类型指定了一个同义字(synonyms)。例如:typedef int Num[100]; Num 是 int [100] 的同义词typedef int (*Pointer)(); Pointer是int(*)() 的同义词2.用typedef只是对已经存在的类型指定一个新的类型名,而没有创造新的类型。3.用tyoedef声明数组类型、指针类型、结构体类型、共用体类型、枚举类型等,使得编程更加方便。4.typedef 与 #define 表面上有相似之处typedef int COUNT#define COUNT int作用都是用COUNT代表int。#define是在预编译时处理,只能作简单的字符串替换, 而typedef是在编译时处理的,为已有类型命名。5.当不同源文件中用到同一类型数据时(数组、结构体等),常用typedef声明一些数据类型。把它们单独放在一个头文件中。6.使用typedef名称有利于程序的通用与移植。有时程序会依赖于硬件特性,用typedef类型就便于移植。
2022年02月25日
409 阅读
0 评论
0 点赞
2022-02-24
C语言链表-学习二十九
链表的概念结构体数组:静态分配存储单元,容易造成内存浪费。链表:是重要的数据结构,它根据需要,动态分配内存单元。特征:头指针变量,存放链表首地址,链表中每个元素称结点。其内容:数据域:可有若干项(整、实、字符、结构体类型等)指针域:下一结点的地址,最后一个结点(表尾)的地址部分为NULL。链表存储数据的空间可以是不连续的,因此对空间的要求和应比较低。链表中结点的空间是在程序执行过程中根据需要随时向系统申请开辟的内存单元,不用时可以随时释放结点所占用的空间。动态分配与数组不同,它不存在空间浪费的问题。例如:void *malloc(unsigned int size)作用:在内存的动态存储区分配一个长度为size的连续空间。void *calloc(unsigned n,unsigned size);作用:在内存动态区分配n个长度为size的连续空间,函数返回指向分配起始地址的指针; 若分配不成功,返回NULL值。void free(void *p)作用:释放由p指向的动态存储区结点之间是通过指针建立先后顺序。链表的插入、删除等操作只要改变个别结点地址部分的指向即可,无需移动大量的数据。对链表的操作必须从头指针开始,然后逐个结点进行访问。链表的声明struct Student { // 结点的数据域 int no; char name[20]; float score; // 结点的指针域 类型是自身结构体类型 struct Student *next; };next是成员名,是指针类型,它指向struct Student数据类型。静态链表在C语言中,静态链表的表现形式为结构体数组,是在程序中定义,不是临时开辟的,也不能用完后释放,每个数组元素包含数据域(data)和指针域(next)。例如#include <stdio.h> struct Student { // 结点的数据域 int no; char name[20]; float score; // 结点的指针域 类型是自身结构体类型 struct Student *next; }; void main() { struct Student head, *p; struct Student stu1 = {10001,"Zhang Yin", 100}; struct Student stu2 = {10002,"Qian Feng", 90}; struct Student stu3 = {10003,"Liu Liang", 91}; head.next = &stu1; // 将stu2的起始地址赋给stu1的next成员 stu1.next = &stu2; stu2.next = &stu3; stu3.next = NULL; for (p = head.next; p!= NULL; p = p->next) { printf("%d\t%s\t%.2f\n",p->no,p->name,p->score); } }动态链表建立链表建立链表是指从无到有的形成一个链表。建立链表的思想就是逐个输入各结点的数据,同时建立结点之间的关系。建立链表的算法概念先开辟一个结点的空间作为头结点,并让头指针指向头结点;然后开辟第一个数据结点,并输入结点数据,将第一结点“挂”在头结点之后;接着开辟第二个数据结点,并输入结点数据,将第二个结点“挂”在第一个结点之后...即按照输入顺序特结点“挂”在一起,形成链表。例子建立一个带有头结点的学生链表,直到输入的学生学号小于等于0时结束。#include <stdio.h> #include <string.h> struct Student { int no; char name[20]; unsigned sex; struct Student *next; }; void main() { void setdata(struct Student *temp); struct Student *creatlink(); void printlink(struct Student *head); struct Student *head; head = creatlink(); printlink(head); } void setdata(struct Student *temp) { printf("No:"); scanf_s("%d", &temp->no); getchar(); printf("Name:"); gets(temp->name); printf("Sex(1:boy;0:girl):"); scanf_s("%d", &temp->sex); } struct Student *creatlink() { int i = 0; struct Student *head, *p, *q; head = (struct Student *)malloc(sizeof(struct Student)); head -> next = NULL; p = head; q = (struct Student *)malloc(sizeof(struct Student)); printf("请输入学生信息:\n"); setdata(q); while ((q->no) > 0) { p->next = q; p = q; q = (struct Student *)malloc(sizeof(struct Student)); printf("请输入学生信息:\n"); setdata(q); } p->next = NULL; return head; } void printlink(struct Student *head) { struct Student *p; printf("-学生信息-\n"); for (p = head->next; p != NULL; p = p->next) { printf("%d\t%s\t%d\n", p->no, p->name, p->sex); } }对链表的删除操作并不真从内存中抹掉,只是把它分离,再前后结点相链接。思路:本例以学号作为删除结点的标志(查找对象)设两个指针变量p1和p2。从head开始,p1依次指向各结点查找num值是否等于要删除结点的学号。每次下移前使p2=p1。学号相等删除该结点,直至查到表尾。struct Student *del(struct Student *head, int no) { struct Student *p1, *p2; if (head == NULL) { printf("\n 链表为空! \n"); return head; } p1 = head; p2 = NULL; while (no != p1->no && p1->next != NULL) { p2 = p1; p1 = p1->next; } if (no == p1->no) { if (p1 == head) { head = p1->next; } else { p2->next = p1->next; } printf("已删除:%d\n", no); } else{ printf("\n没有找到要删除的对象:%d\n", no); } return(head); } 对链表的插入操作思路:找到插入点后,将该点的next值指向新结点,并使新结点的next值等于断点后面结点的首地址。struct Student *insert(struct Student *head, struct Student *stud) { struct Student *p0, *p1, *p2; p1 = head; // 使p1指向第一个结点 p0 = stud; // p0指向要插入的结点 p2 = NULL; if (head == NULL) { // 原来的链表是空表 head = p0; p0 -> next = NULL; // 使p0指向的结点 } else { while ((p0->no > p1->no) && (p1->next != NULL)) { p2 = p1; // 使p2指向刚才p1指向的结点 p1 = p1->next; // p1后移一个结点 } if (p0->no <= p1->no) { if (head == p1) { head = p0; //插到原来第一个结点之前 } else { p2->next = p0; // 插到p2指向的结点之后 } p0->next = p1; } else { p1->next = p0; // 插到最后一个结点之后 p0->next = NULL; } return(head); } }综合例子#include <stdio.h> #include <string.h> struct Student { int no; char name[20]; unsigned sex; struct Student *next; }; void main() { void setdata(struct Student *temp); struct Student *creatlink(); void printlink(struct Student *head); struct Student *del(struct Student *head, int no); struct Student *insert(struct Student *head, struct Student *stud); struct Student *head, *stu; int num; head = creatlink(); printlink(head); printf("请输入要删除的学号:"); scanf_s("%d", &num); while (num != 0) { // 为0退出删除 head = del(head, num); printlink(head); printf("请输入要删除的学号:"); scanf_s("%d", &num); } printf("请输入要插入的信息:\n"); stu = (struct Student *)malloc(sizeof(struct Student)); setdata(stu); while (stu->no != 0) { head = insert(head, stu); printlink(head); printf("请输入要插入的信息:\n"); stu = (struct Student *)malloc(sizeof(struct Student)); setdata(stu); } } void setdata(struct Student *temp) { printf("No:"); scanf_s("%d", &temp->no); getchar(); printf("Name:"); gets(temp->name); printf("Sex(1:boy;0:girl):"); scanf_s("%d", &temp->sex); } struct Student *creatlink() { int i = 0; struct Student *head, *p, *q; head = (struct Student *)malloc(sizeof(struct Student)); head -> next = NULL; p = head; q = (struct Student *)malloc(sizeof(struct Student)); printf("请输入学生信息:\n"); setdata(q); while ((q->no) > 0) { p->next = q; p = q; q = (struct Student *)malloc(sizeof(struct Student)); printf("请输入学生信息:\n"); setdata(q); } p->next = NULL; return head; } void printlink(struct Student *head) { struct Student *p; printf("-学生信息-\n"); for (p = head->next; p != NULL; p = p->next) { printf("%d\t%s\t%d\n", p->no, p->name, p->sex); } } struct Student *del(struct Student *head, int no) { struct Student *p1, *p2; if (head == NULL) { printf("\n 链表为空! \n"); return head; } p1 = head; p2 = NULL; while (no != p1->no && p1->next != NULL) { p2 = p1; p1 = p1->next; } if (no == p1->no) { if (p1 == head) { head = p1->next; } else { p2->next = p1->next; } printf("已删除:%d\n", no); } else{ printf("\n没有找到要删除的对象:%d\n", no); } return(head); } struct Student *insert(struct Student *head, struct Student *stud) { struct Student *p0, *p1, *p2; p1 = head; // 使p1指向第一个结点 p0 = stud; // p0指向要插入的结点 p2 = NULL; if (head == NULL) { // 原来的链表是空表 head = p0; p0 -> next = NULL; // 使p0指向的结点 } else { while ((p0->no > p1->no) && (p1->next != NULL)) { p2 = p1; // 使p2指向刚才p1指向的结点 p1 = p1->next; // p1后移一个结点 } if (p0->no <= p1->no) { if (head == p1) { head = p0; //插到原来第一个结点之前 } else { p2->next = p0; // 插到p2指向的结点之后 } p0->next = p1; } else { p1->next = p0; // 插到最后一个结点之后 p0->next = NULL; } return(head); } }
2022年02月24日
409 阅读
0 评论
0 点赞
2022-02-23
C语言结构体数组、指针与函数-学习二十八
结构体数组结构体数组的定义一个结构体变量只能存放一组有关联的数据,比如一个学生的数据,如果要存放多个学生的数据,就要使用结构体数组。在声明了结构体类型之后,结构体数组的定义与基本类型数组的定义完全相同。例如:struct student{ long int no; char name[20]; char sex; float score; }; struct student stu[3];定义了struct student类型的数组,数组名为stu,包含3个数组元素,每个数组元素都是一个struct student类型的结构体变量,分别是stu[0].stu[1].stu[2]。结构体数组的引用产表示下标为i的结构体数组元素的成员no的值stu[i].no;产表示下标为i的结构体数组元素的成员name的首地址(name为char数组)stu[i].name;表示下标为i+1的结构体数组元素的成员sex值stu[i+ 1].sex;产表示下标为i- 1的结构体数组元素的成员score的地址&stu[i- 1].score;结构体数组的初始化结构体数组和基本类型数组一样可以初始化,只是每个元素的初值是由括起来的一组数据,初始化的形式是定义数组的同时,在其后面加上“={初值表列}”。例如:struct student{ int num; char name[20]; char sex; int age; } struct student stu[3]={ {100,"Wang Lin",M',20}, {101,"Li Gang", 'M',19}, {110,"Liu Yan", F,19} }; 例子有3个候选人,每个选民只能投票选一人,要求编一个统计选票的程序,先后输入被选人的名字,进行投票,最后输出各人得票结果。#include <stdio.h> #include <string.h> void main() { struct Person { char name[20]; int count; }leader[3] = { {"Zhangsan",0},{"Lisi",0},{"Wangwu",0} }; int i, j; char leader_name[20]; printf("请输入支持的候选人名字:\n"); for (i = 0; i < 10; i++) { scanf_s("%s", leader_name,20); for (j = 0; j < 3; j++) { // strcmp函数比较字符串是否相同,相同就返回 0 if (strcmp(leader_name, leader[j].name) == 0) { leader[j].count++; } } } printf("\n"); for (i = 0; i < 3; i++) { printf("%s:%d\n", leader[i].name, leader[i].count); } }有n个学生的信息(包括学号、姓名、成绩),要求按照成绩的高低顺序输出各学生的信息。#include <stdio.h> struct Student { int no; char name[20]; float score; }; void main() { struct Student stu[5] = { {10001,"Zhangsan",98.5}, {10002,"Lisi",80.5}, {10003,"Wangwu",80.5}, {10004,"Zhaoliu",100}, {10005,"Xiqi",59} }; struct Student temp; // 定义常量 const int n = 5; int i, j, k; printf("成绩排行榜\n"); for (i = 0; i < n; i++) { for (j = i + 1; j < n; j++) { if (stu[j].score>stu[i].score) { temp = stu[j]; stu[j] = stu[i]; stu[i] = temp; } } } for (i = 0; i < n; i++) { printf("%d %10s %.2f\n",stu[i].no,stu[i].name,stu[i].score); } }结构体指针指向结构体变量的指针指向结构体对象的指针变量既可以指向结构体变量,也可以用来指向结构体数组中的元素。指针变量的基类型必须与结构体变量的类型相同。定义格式:struct 结构体名 *结构体指针变量名例如:struct Student stu; struct Student *pt;赋值pt = &stu;1.结构体类型只能表示一个结构形式,编译程序并不对它分配内存单元。2.定义指向结构体类型的指针变量并确定它的指向后,就可以用结构体指针变量访问所指向的结构体的成员。指针变量访问结构体变量的成员有以下几种形式(1)(*结构体指针变量).成员名 括号不能省略,因为 “.” 运算符优先级高于 “*” 的例如:(*sp).name(2)结构体指针变量 -> 成员名C语言提供了一种简便的结构体指针变量取成员运算 “->”称为指向成员运算符(或箭头运算符),和 “.” 优先级差不多,结合性都是从左往右。例如: sp -> name(&结构体变量) -> 成员名例如:(&stu) -> namae当用结构体变量访问结构体数据成员时用 “.” 运算符比较方便;当用指针访问结构体成员时用 “->” 运算符比较方便。例如sp -> nosp指向结构体变量stu, sp -> no 等价于 stu.nosp -> no++ 指向运算符 “->” 高于 “++” ,等价于 (sp -> no)++ 。先引用sp -> no值,即 stu.no 作为表达式的值;再使其值加1,即stu.no值加1。++sp -> no指向运算符 “->” 高于 “++” ,等价于 ++(sp -> no) 即先使 sp->no 值加1,即 stu.no 值加1;然后引用修改之后的 sp -> no值作为表达式的值。例子#include <stdio.h> struct Student { int no; char name[20]; float score; }; void main() { struct Student *p; struct Student stu = {10001,"Zhangsan",98.5}; p = &stu; printf("学号:%d\n姓名:%s\n成绩:%.2f\n",(*p).no,p -> name,(&stu) -> score); }指向结构体数组的指针可以用指针变量指向结构体数组的元素。例如:有3个学生的信息,放在结构体数组中,要求全部学生的信息。#include <stdio.h> struct Student { int no; char name[20]; float score; }; void main() { struct Student *p; struct Student stu[5] = { {10001,"Zhangsan",98.5}, {10002,"Lisi",80.5}, {10003,"Wangwu",80.5}, {10004,"Zhaoliu",100}, {10005,"Xiqi",59} }; for (p = stu; p<stu+5; p++) { printf("%d %10s %.2f\n",p->no, p->name, p->score); } }用结构体变量和结构体变量的指针作函数结构体作为函数参数对结构体数据操作时,常常需要将结构体变量的成员、结构体变量、结构体数组名或结构体指针作为参数 传递给另一个函数。1.用结构体变量的成员作参数用法和用普通变量作实参是一样的,属于“值传递”方式。应当注意实参与形参的类型保持一致。例如:用 stu[1].num 或 stu[2].name 作函数实参,将实参值传给形参。2.用结构体变量作实参用结构体变量作实参时,将结构体变量所占的内存单元的内容全部按顺序传递给形参。形参也必须是同类型的结构体变量。在函数调用期间形参也要占用内存单元,这种传递方式在空间和时间上开销较大。在被调用函数期间改变形参(也是结构体变量)的值,不能返回主调函数。3.用指向结构体变量(或数组元素)的指针作实参用指向结构体变量(或数组元素)的指针作实参时,将结构体变量(或数组元素)的地址传递给形参。结构体与函数之间的关系主要是:1.结构体作为函数的参数。2.结构体可以作为函数的返回值。例子:有n个结构体变量,内含学生学号、姓名和3门课程的成绩。要求输出平均成绩最高的学生的信息(包括学号、姓名、3门课程成绩和平均成绩)。#include <stdio.h> #define N 3 struct Student { int no; char name[20]; float score[3]; float aver; }; void main() { void input(struct Student stu[]); struct Student max(struct Student stu[]); void print(struct Student stu); struct Student *p; struct Student stu[N]; p = stu; input(p); print(max(p)); } void input(struct Student stu[]) { int i; printf("请输入各学生信息:\n"); for (i = 0; i < N; i++) { scanf_s("%d %s %f %f %f", &stu[i].no, stu[i].name,20, &stu[i].score[0], &stu[i].score[1], &stu[i].score[2]); stu[i].aver = (stu[i].score[0] + stu[i].score[1] + stu[i].score[2]) / 3.0; } } struct Student max(struct Student stu[]) { int i,m=0; int temp; for (i = 0; i < N; i++) { if (stu[i].aver > stu[m].aver) { m = i; } } return stu[m]; } void print(struct Student stud) { printf("学号:%d\n姓名:%s\n三门成绩:%.2f %.2f %.2f\n平均成绩:%.2f\n", stud.no, stud.name, stud.score[0], stud.score[1], stud.score[2], stud.aver); }
2022年02月23日
426 阅读
0 评论
0 点赞
1
2
...
7