新叶集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()函数清除这个错误标志。

浙公网安备 33010602011771号