新叶集1

新叶集1

汇总了《C语言程序设计》中(对笔者而言的)新知识。

一、结构体位段

掌握:结构体位段的特性、定义含位段的结构体变量、赋值和引用位段成员。

作用

一般而言,数据存取都是以字节(Byte : 1 Byte = 8 bits)为单位的。但有时存储一个数据仅需若干个二进制位(bit)即可,从而节省存储空间

定义

struct packed_data
{
	unsigned short a:2;	//指定成员a占2位
	unsigned short b:6;	//指定成员b占6位
	unsigned short c:5;	//指定成员c占5位
	unsigned short d:3;	//指定成员d占3位
	short int i;	//指定成员i为短整型,占16位
}data;	//定义结构体类型变量data

变量data的内存分配如图1,共占4字节。

位段的存取方法与结构体成员的存取方法相同。

若有:data.a = 2 , data.b = 37 , data.c = 28 , data.d = 6 , data.i = 300 ,则内存数据图如图2。

赋值

给位段成员赋值时,应注意成员的取值范围,取值范围由位段占几位决定。

注意事项

(1)一个位段的长度不能超过一个存储单元(8位)。

(2)一个位段不能跨越两个存储单元。若当前存储单元剩余的二进制位数小于位段长度,则剩余的二进制位不用,系统自动到下一个存储单元为该位段分配空间(不够用,跳下一个)。

(3)不想用的二进制位可以定义为无名位段,例如

struct packed_data
{
	unsigned short a:2;
	unsigned short :6;	//无名位段,占6位,不用
	unsigned short c:5;
	unsigned short d:3;
	short int i;
}data;

(4)设置长度为0的位段,使得下一个位段从下一字节开始存放,例如

struct packed_data
{
	unsigned short a:2;
	unsigned short b:0;	//位段长度为0
	unsigned short c:5;	//位段c从下一字节开始存放
	unsigned short d:3;
	short int i;
}data;

(5)位段成员的类型必须指定无符号整型

(6)位段数据可以按整型数据引用和输出。

(7)不能定义位段数组

实例

由此可见,分析结构体位段,首先要计算结构体变量所占空间,再根据结构体成员所占空间和赋的值,画出内存数据图。

若需将整个结构体变量按十六进制输出,则可把内存数据图每四位一分段,每段对应一个十六进制数。

二、共用体类型

掌握:定义共用体类型、定义和引用共用体变量。

作用

不同类型的数据存放到同一段内存中,这些数据起始地址相同,互相覆盖,只有最后一次存入的数据才有效。

定义共用体类型

union 共用体类型名
{
    类型1 成员1;
    类型2 成员2;
    ...
    类型n 成员n;
};

在定义形式上,共用体和结构体只有union和struct的差别。

定义共用体变量

三种方式,同结构体变量的定义。(顺便复习结构体了)

(1)先定义共用体类型,再定义共用体变量

union data
{
	short int i;
	char ch;
	float f;
};
union data a,b,c;

(2)定义共用体类型的同时定义共用体变量

union data
{
	short int i;
	char ch;
	float f;
}a,b,c;

(3)定义无名共用体类型的同时定义共用体变量(自然,这种形式的共用体类型无法复用)

union
{
	short int i;
	char ch;
	float f;
}a,b,c;

引用共用体变量

共用体变量的成员是叠放在同一个地址开始的空间上的。分析共用体问题时,依旧建议画出内存分布图(从右往左开空间)。

引用共用体成员,需要引用到最底层的成员。形式:

共用体变量名.成员名[.成员名.…]

实例:

#include<stdio.h>
union data
{
	char c;
	short int s;
	long int i;
};
int main()
{
	union data ua;
	ua.i=0x11223344;
	printf("c=%x\ts=%x\ti=%lx\n",ua.c,ua.s,ua.i);
	ua.s=0x5566;
	printf("c=%x\ts=%x\ti=%lx\n",ua.c,ua.s,ua.i);
	ua.c=0x77;
	printf("c=%x\ts=%x\ti=%lx\n",ua.c,ua.s,ua.i);
	return 0;
}
/*
运行结果
c=44	s=3344	i=11223344
c=66	s=5566	i=11225566
c=77	s=5577	i=11225577
*/

注意事项

(1)共用体变量及其所有成员的起始地址相同,&ua、&ua.c、&ua.s、&ua.i都是共用体变量ua的指针。

(2)不能对共用体变量进行初始化,下列初始化是错误的:

union data ua={'a',1,100};

(3)不能用常量对共用体变量进行赋值,下列赋值是错误的:

ua=100;

(4)在某一时刻,共用体变量只有一个成员起作用,即最后被赋值的成员

三、枚举类型

掌握:定义枚举类型,给枚举变量赋值,输出枚举元素。

作用

当变量的取值被限定在一个有限的范围内时(如一周7天,一年12个月),可使用枚举类型。枚举是指变量的值屈指可数,可以一一列举出来,变量取值仅限于所列值

定义枚举类型

enum 枚举类型名
{
	枚举常量 1[=序号1],
	枚举常量 2[=序号2],
	...
	枚举常量 n[=序号n]
};

枚举常量枚举元素

实例:

enum weekday{sun,mon,tue,wed,thu,fri,sat}yesterday,today,tomorrow;
  • 名词解析:weekday是枚举类型,sun、mon...等等是枚举常量,又名枚举元素,yesterday、today、tomorrow是枚举变量

序号

若省略序号,则系统默认从0开始连续排列,上述实例7个枚举元素序号依次为:0、1、2、3、4、5、6。

若遇到有改变的序号,则序号从被改变的位置开始连续递增。例如这样改写:

enum weekday{sun,mon=6,tue,wed,thu=20,fri,sat};

则7个枚举元素的序号依次为:0、6、7、8、20、21、22。

序号的作用:指代枚举元素,相当于枚举元素的代号。

enum weekday{sun,mon,tue,wed,thu,fri,sat}yesterday,today,tomorrow;为例:

today=mon;等价于today=(enum weekday)1;

枚举变量的赋值

(1)可以使用枚举元素给枚举型变量赋值,如

today=sun;

(2)枚举型变量之间可以赋值,如

yesterday=today;

(3)枚举元素是常量,不能对枚举元素进行赋值(定义类型时除外)。

(4)欲将整数赋值给枚举变量,则必须进行强制类型转换,如

today=(enum weekday)5;

枚举变量的运算

(1)枚举变量和枚举元素都可以进行关系运算,比较时使用的是其序号。若有赋值语句“today=sun;”,则“today==sat”的结果为假。

(2)枚举变量可以进行自加自减运算。

输出枚举元素

不能直接输出枚举元素。

通常使用switch语句与字符串输出函数实现。先用switch语句判断枚举类型等于哪个枚举元素(或序号),再用字符串输出函数输出相应的标识符。

#include<stdio.h>
enum weekday{sun,mon,tue,wed,thu,fri,sat};
int main()
{
	enum weekday yesterday,today,tomorrow;
	yesterday=fri;//用枚举元素给枚举变量赋值 
	today=yesterday;//枚举变量之间可以赋值 
	today++;//枚举变量可以自加自减 
	tomorrow=(enum weekday)0;//整数给枚举变量赋值时,要使用强制类型转换
	switch(yesterday)
	{
		case sun:printf("%s","sun");break;
		case mon:printf("%s","mon");break;
		case tue:printf("%s","tue");break;
		case wed:printf("%s","wed");break;
		case thu:printf("%s","thu");break;
		case fri:printf("%s","fri");break;
		case sat:printf("%s","sat");break;
	}
	printf("\n");
	switch(today)
	{
		case sun:printf("%s","sun");break;
		case mon:printf("%s","mon");break;
		case tue:printf("%s","tue");break;
		case wed:printf("%s","wed");break;
		case thu:printf("%s","thu");break;
		case fri:printf("%s","fri");break;
		case sat:printf("%s","sat");break;
	}
	printf("\n");
	switch(tomorrow)
	{
		case sun:printf("%s","sun");break;
		case mon:printf("%s","mon");break;
		case tue:printf("%s","tue");break;
		case wed:printf("%s","wed");break;
		case thu:printf("%s","thu");break;
		case fri:printf("%s","fri");break;
		case sat:printf("%s","sat");break;
	}
	printf("\n");
}

四、文件系统

掌握:定义文件类型指针变量,文件的打开与关闭,文件的读写操作,文件定位与随机读写。

概述

  • 原则:按名存取。

  • 根据数据的存放形式,文件分为ASCII码文件和二进制文件,ASCII码文件又称文本文件或正文文件。

  • C语言文件处理系统:缓冲区文件系统和非缓冲区文件系统。

  • 使用文件:通过一个结构体类型的指针(FILE *fp)与文件建立联系,从而对文件进行操作控制。

  • EOF(值为-1)是文本文件结束的标志。

  • 文件路径应使用双斜杠(从左往右),因为它是转义字符,表示单斜杠。

定义文件类型指针变量

FILE *fp;

下文所有的fp都以此方式定义。

文件的打开与关闭

文件的打开方式

文件打开方式 含义
r(只读文本) 为读打开一个文本文件
w(只写文本) 为写打开一个文本文件
a(追加文本) 文本文件尾部追加数据
rb(只读二进制) 为读打开一个二进制文件
wb(只写二进制) 为写打开一个二进制文件
ab(追加二进制) 二进制文件尾部追加数据
r+(读写文本) 为读/写打开一个文本文件
w+(读写文本) 为读/写建立一个新的文本文件
a+(读写文本) 为读/写打开一个文本文件
rb+(读写二进制) 为读/写打开一个二进制文件
wb+(读写二进制) 为读/写建立一个新的二进制文件
ab+(读写二进制) 为读/写打开一个二进制文件

说明:

(1)r表示只读,w表示只写,a表示追加,b表示二进制,+表示可读可写。

(2)以r打开文件,文件必须存在;以w打开文件,如果文件不存在,则先建立文件,否则先删除、再新建;以a打开文件,如果文件不存在,则先建立文件,否则文件中的位置指针会移到文件末尾,准备追加数据。

(3)向文本文件写入数据时,系统自动将回车换行符转换为一个换行符;从文本文件读出数据时,系统又将换行符转换成回车和换行两个字符。二进制文件不进行这种转换。

文件打开函数

fp=fopen(文件名,"打开方式")

打开文件规定了三个操作:一是打开哪个文件,二是以何种方式打开该文件,三是与哪个文件指针建立联系。

若打开失败,fopen会返回一个空指针NULL,故常用以下方式打开文件:

if((fp=fopen(文件名,"打开方式"))==NULL)
{
	printf("Cannot open this file!\n");
	exit(0);
}

文件关闭函数

fclose(fp)

文件读写操作

读写一个字符

下文ch为用户定义的字符型变量。

ch=fgetc(fp)

fputc(ch,fp)

实例:(程序功能为从键盘输入的一组字符,遇到”#“结束,写入磁盘文件,并将写入文件中的字符再输出到屏幕上)

#include<stdio.h>
#include<stdlib.h>
int main()
{
	FILE *fp;
	char ch,filename[10];
	printf("Input filename:"); scanf("%s",filename);
	if((fp=fopen(filename,"w"))==NULL)//创建名为“filename”的文件,fp指向该文件
	{
		printf("Cannot create this file!\n");
		exit(0);
	}
	printf("Please input text(#==End):\n");
	while((ch=getchar())!='#')//从键盘读入ch
		fputc(ch,fp);//将ch输出到fp所指的文件中
	fclose(fp);
	if((fp=fopen(filename,"r"))==NULL)
	{
		printf("Cannot create this file!\n");
		exit(0);
	}
	while((ch=fgetc(fp))!=EOF)
		putchar(ch);
	fclose(fp);
	return 0;
}

判断文件是否结束feof(fp)

上述程序中的

while((ch=fgetc(fp))!=EOF)
	putchar(ch);

可改为

while(!feof(fp))
	putchar(fgetc(fp));

读写一个字符串

fgets(str,n,fp)

从fp指向的文件中读取一个长度至多为n-1的字符串,并存入从str开始的内存单元中,结尾自动加'\0'。

fputs(str,fp)

将str所指的字符串写入fp指向的文件中,遇到'\0'自动结束。

实例:(程序功能为将键盘输入的4行字符串写入磁盘文件中,再将写入文件的字符串输出到屏幕上)

#include<stdio.h>
#include<stdlib.h>
#define N 4
int main()
{
	FILE *fp;
	char ch[80],filename[20];
	int i;
	printf("Input filename:");
	gets(filename);
	if((fp=fopen(filename,"w"))==NULL)
	{
		printf("Cannot create this file!\n");
		exit(0);
	}
	printf("Input %d line strings:\n",N);
	for(i=1;i<=N;i++)
	{
		gets(ch);
		fputs(ch,fp);
		fputc('\n',fp);
	}
	fclose(fp);
	if((fp=fopen(filename,"r"))==NULL)
	{
		printf("Cannot open this file!\n");
		exit(0);
	}
	printf("From file: %s\n",filename);
	for(i=1;i<=N;i++)
	{
		fgets(ch,80,fp);
		printf("%s",ch);
	}
	fclose(fp);
	return 0;
}

读写一个数据字块

比如读写一个数组。

下文中buffer是数据的存放地址,size是一个数据块的大小(字节数),count是数据块的数目。

fread(buffer,size,count,fp)

从fp指向的文件中读出count个大小为size字节的数据,并存入从buffer开始的内存单元。

例如:

float f[2];
fread(f,4,2,fp);

fwrite(buffer,size,count,fp)

把从buffer开始的count个大小为size字节的数据写入fp指向的文件中。

一般地,用fread()和fwrite()进行读写时,文件应以二进制方式打开。

实例:(程序功能为将一个实数及5个整数写入文件TEXT.txt中,再读出并显示)

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int a[5]={10,20,30,40,50},b[5],i;
	float f=3.14159,ff;
	FILE *fp;
	if((fp=fopen("file73.txt","wb"))==NULL)
	{
		printf("Cannot open this file!\n");
		exit(0);
	}
	fwrite(&f,sizeof(float),1,fp);
	fwrite(a,sizeof(int),5,fp);
	fclose(fp);
	if((fp=fopen("file73.txt","rb"))==NULL)
	{
		printf("Cannot open this file\n");
		exit(0);
	}
	fread(&ff,sizeof(float),1,fp);
	fread(b,sizeof(a),1,fp);
	printf("ff=%f\nb=\t",ff);
	for(i=0;i<5;i++) printf("%d\t",b[i]);
	printf("\n");
	fclose(fp);
	return 0;
}

格式化读写

fscanf(fp,"格式控制字符串",地址表列)

例如:

int a,b;
fscanf(fp,"%d %d",&a,&b);	//从fp指向的文件中读取两个整数,分别赋值给变量a、b
fscanf(stdin,"%d %d",&a,&b);	//等价于scanf("%d %d",&a,&b);

格式控制字符串的安排自然应该与文件中数据存放的格式一致。

fprintf(fp,"格式控制字符串",输出表列)

例如

int a=3,b=4;
fprintf(fp,"a=%d\tb=%d",a,b);	//将a、b的值按照指定格式写入fp指向的文件
fprintf(stdout,"a=%d\tb=%d",a,b);	//等价于printf("a=%d\tb=%d",a,b);

文件定位与随机读写

位置指针复位 函数

rewind(fp)

将fp指向的文件的位置指针置于文件的开头

位置指针随机定位 函数

fseek(fp,位移量,起始点)

在fp指向的文件中,以“起始点”为基点,将位置指针向前(文件尾方向)或向后(文件头方向)移动“位移量”个字节的距离。

文件位置描述符

起始点 常量名标识 数字表示
文件开始 SEEK_SET 0
文件当前位置 SEEK_CUR 1
文件末尾 SEEK_END 2

例如:

fseek(fp,100L,0);	//以文件头为基点,向前移动100字节
fseek(fp,-4L,1);	//以当前位置为基点,向后移动4字节
fseek(fp,-10L,SEEK_END);	//以文件尾为基点,向后移动10字节

检测当前位置指针的位置 函数

ftell(fp)

检测当前位置指针的位置距离头文件有多少字节的距离。

失败返回值-1L。

错误处理 函数

(1)文件操作出错测试函数

ferror(fp)

出错返回非0,否则返回0。

(2)清除错误标志函数

clearerr(fp)

将文件出错标志置为0。

  • 只要出现错误标志,就会一直保留。因此在使用完ferror()后应立即使用clearerr()函数清除这个错误标志。
posted @ 2026-01-28 19:31  Fish4174  阅读(4)  评论(0)    收藏  举报