饭饭TXT > 学习管理 > 《C语言程序设计》作者:谭浩强【完结】 > 【书香门第】C语言程序设计.txt

第 13 页

作者:谭浩强 当前章节:14985 字 更新时间:2026-6-23 04:45

可在第一个结点的指针域内存入第二个结点的首地址,在第二个结点的指针域内又存放第三个结点的首地址,如此串连下去直到最后一个结点。最后一个结点因无后续结点连接,其指针域可赋为0。这样一种连接方式,在数据结构中称为“链表”。

下图为最一简单链表的示意图。

图中,第0个结点称为头结点,它存放有第一个结点的首地址,它没有数据,只是一个指针变量。以下的每个结点都分为两个域,一个是数据域,存放各种实际的数据,如学号num,姓名name,性别sex和成绩score等。另一个域为指针域,存放下一结点的首地址。链表中的每一个结点都是同一种结构类型。

例如,一个存放学生学号和成绩的结点应为以下结构:

struct stu

{ int num;

int score;

struct stu *next;

}

前两个成员项组成数据域,后一个成员项next构成指针域,它是一个指向stu类型结构的指针变量。

链表的基本操作对链表的主要操作有以下几种:

1. 建立链表;

2. 结构的查找与输出;

3. 插入一个结点;

4. 删除一个结点;

下面通过例题来说明这些操作。

【例11.9】建立一个三个结点的链表,存放学生数据。为简单起见, 我们假定学生数据结构中只有学号和年龄两项。可编写一个建立链表的函数creat。程序如下:

#define NULL 0

#define TYPE struct stu

#define LEN sizeof (struct stu)

struct stu

{

int num;

int age;

struct stu *next;

};

TYPE *creat(int n)

{

struct stu *head,*pf,*pb;

int i;

for(i=0;i<n;i++)

{

pb=(TYPE*) malloc(LEN);

printf("input Number and Age\n");

scanf("%d%d",&pb->num,&pb->age);

if(i==0)

pf=head=pb;

else pf->next=pb;

pb->next=NULL;

pf=pb;

}

return(head);

}

在函数外首先用宏定义对三个符号常量作了定义。这里用 TYPE表示struct stu,用LEN表示sizeof(struct stu)主要的目的是为了在以下程序内减少书写并使阅读更加方便。结构stu定义为外部类型,程序中的各个函数均可使用该定义。

creat函数用于建立一个有n个结点的链表,它是一个指针函数,它返回的指针指向stu结构。在creat函数内定义了三个stu结构的指针变量。head为头指针,pf为指向两相邻结点的前一结点的指针变量。pb为后一结点的指针变量。

11.10 枚举类型

在实际问题中,有些变量的取值被限定在一个有限的范围内。例如,一个星期内只有七天,一年只有十二个月,一个班每周有六门课程等等。如果把这些量说明为整型,字符型或其它类型显然是不妥当的。为此,C语言提供了一种称为“枚举”的类型。在“枚举”类型的定义中列举出所有可能的取值,被说明为该“枚举”类型的变量取值不能超过定义的范围。应该说明的是,枚举类型是一种基本数据类型,而不是一种构造类型,因为它不能再分解为任何基本类型。

11.10.1 枚举类型的定义和枚举变量的说明

1. 枚举的定义枚举类型定义的一般形式为:

enum 枚举名{ 枚举值表 };

在枚举值表中应罗列出所有可用值。这些值也称为枚举元素。

例如:

该枚举名为weekday,枚举值共有7个,即一周中的七天。凡被说明为weekday类型变量的取值只能是七天中的某一天。

2. 枚举变量的说明

如同结构和联合一样,枚举变量也可用不同的方式说明,即先定义后说明,同时定义说明或直接说明。

设有变量a,b,c被说明为上述的weekday,可采用下述任一种方式:

enum weekday{ sun,mou,tue,wed,thu,fri,sat };

enum weekday a,b,c;

或者为:

enum weekday{ sun,mou,tue,wed,thu,fri,sat }a,b,c;

或者为:

enum { sun,mou,tue,wed,thu,fri,sat }a,b,c;

11.10.2 枚举类型变量的赋值和使用

枚举类型在使用中有以下规定:

1. 枚举值是常量,不是变量。不能在程序中用赋值语句再对它赋值。

例如对枚举weekday的元素再作以下赋值:

sun=5;

mon=2;

sun=mon;

都是错误的。

2. 枚举元素本身由系统定义了一个表示序号的数值,从0开始顺序定义为0,1,2…。如在weekday中,sun值为0,mon值为1,…,sat值为6。

【例11.10】

main(){

enum weekday

{ sun,mon,tue,wed,thu,fri,sat } a,b,c;

a=sun;

b=mon;

c=tue;

printf("%d,%d,%d",a,b,c);

}

说明:

只能把枚举值赋予枚举变量,不能把元素的数值直接赋予枚举变量。如:

a=sum;

b=mon;

是正确的。而:

a=0;

b=1;

是错误的。如一定要把数值赋予枚举变量,则必须用强制类型转换。

如:

a=(enum weekday)2;

其意义是将顺序号为2的枚举元素赋予枚举变量a,相当于:

a=tue;

还应该说明的是枚举元素不是字符常量也不是字符串常量,使用时不要加单、双引号。

【例11.11】

main(){

enum body

{ a,b,c,d } month[31],j;

int i;

j=a;

for(i=1;i<=30;i++){

month[i]=j;

j++;

if (j>d) j=a;

}

for(i=1;i<=30;i++){

switch(month[i])

{

case a:printf(" %2d %c\t",i,'a'); break;

case b:printf(" %2d %c\t",i,'b'); break;

case c:printf(" %2d %c\t",i,'c'); break;

case d:printf(" %2d %c\t",i,'d'); break;

default:break;

}

}

printf("\n");

}

11.11 类型定义符typedef

C语言不仅提供了丰富的数据类型,而且还允许由用户自己定义类型说明符,也就是说允许由用户为数据类型取“别名”。类型定义符typedef即可用来完成此功能。例如,有整型量a,b,其说明如下:

int a,b;

其中int是整型变量的类型说明符。int的完整写法为integer,为了增加程序的可读性,可把整型说明符用typedef定义为:

typedef int INTEGER

这以后就可用INTEGER来代替int作整型变量的类型说明了。

例如:

INTEGER a,b;

它等效于:

int a,b;

用typedef定义数组、指针、结构等类型将带来很大的方便,不仅使程序书写简单而且使意义更为明确,因而增强了可读性。

例如:

typedef char NAME[20]; 表示NAME是字符数组类型,数组长度为20。然后可用NAME 说明变量,如:

NAME a1,a2,s1,s2;

完全等效于:

char a1[20],a2[20],s1[20],s2[20]

又如:

typedef struct stu

{ char name[20];

int age;

char sex;

} STU;

定义STU表示stu的结构类型,然后可用STU来说明结构变量:

STU body1,body2;

typedef定义的一般形式为:

typedef 原类型名 新类型名

其中原类型名中含有定义部分,新类型名一般用大写表示,以便于区别。

有时也可用宏定义来代替typedef的功能,但是宏定义是由预处理完成的,而typedef则是在编译时完成的,后者更为灵活方便。

  语言12

12 位运算

12.1 位运算符C语言提供了六种位运算符:

12.1.1 按位与运算

12.1.2 按位或运算

12.1.3 按位异或运算

12.1.4 求反运算

12.1.5 左移运算

12.1.6 右移运算

12.2 位域(位段)

12.3 本章小结

12位运算

前面介绍的各种运算都是以字节作为最基本位进行的。但在很多系统程序中常要求在位(bit)一级进行运算或处理。C语言提供了位运算的功能,这使得C语言也能像汇编语言一样用来编写系统程序。

12.1位运算符C语言提供了六种位运算符:

&按位与

|按位或

^按位异或

~取反

<<左移

>>右移

12.1.1按位与运算

按位与运算符"&"是双目运算符。其功能是参与运算的两数各对应的二进位相与。只有对应的两个二进位均为1时,结果位才为1,否则为0。参与运算的数以补码方式出现。

例如:9&5可写算式如下:

00001001(9的二进制补码)

&00000101(5的二进制补码)

00000001(1的二进制补码)

可见9&5=1。

按位与运算通常用来对某些位清0或保留某些位。例如把a的高八位清0,保留低八位,可作a&255运算(255的二进制数为0000000011111111)。

【例12.1】

main(){

inta=9,b=5,c;

c=a&b;

printf("a=%d\nb=%d\nc=%d\n",a,b,c);

}

12.1.2按位或运算

按位或运算符“|”是双目运算符。其功能是参与运算的两数各对应的二进位相或。只要对应的二个二进位有一个为1时,结果位就为1。参与运算的两个数均以补码出现。

例如:9|5可写算式如下:

00001001

|00000101

00001101(十进制为13)可见9|5=13

【例12.2】

main(){

inta=9,b=5,c;

c=a|b;

printf("a=%d\nb=%d\nc=%d\n",a,b,c);

}

12.1.3按位异或运算

按位异或运算符“^”是双目运算符。其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。参与运算数仍以补码出现,例如9^5可写成算式如下:

00001001

^00000101

00001100(十进制为12)

【例12.3】

main(){

inta=9;

a=a^5;

printf("a=%d\n",a);

}

12.1.4求反运算

求反运算符~为单目运算符,具有右结合性。其功能是对参与运算的数的各二进位按位求反。

例如~9的运算为:

~(0000000000001001)结果为:1111111111110110

12.1.5左移运算

左移运算符“<<”是双目运算符。其功能把“<<”左边的运算数的各二进位全部左移若干位,由“<<”右边的数指定移动的位数,高位丢弃,低位补0。

例如:

a<<4

指把a的各二进位向左移动4位。如a=00000011(十进制3),左移4位后为00110000(十进制48)。

12.1.6右移运算

右移运算符“>>”是双目运算符。其功能是把“>>”左边的运算数的各二进位全部右移若干位,“>>”右边的数指定移动的位数。

例如:

设a=15,

a>>2

表示把000001111右移为00000011(十进制3)。

应该说明的是,对于有符号数,在右移时,符号位将随同移动。当为正数时,最高位补0,而为负数时,符号位为1,最高位是补0或是补1取决于编译系统的规定。TurboC和很多系统规定为补1。

【例12.4】

main(){

unsigneda,b;

printf("inputanumber:");

scanf("%d",&a);

b=a>>5;

b=b&15;

printf("a=%d\tb=%d\n",a,b);

}

请再看一例!

【例12.5】

main(){

chara='a',b='b';

intp,c,d;

p=a;

p=(p<<8)|b;

d=p&0xff;

c=(p&0xff00)>>8;<BR>printf("a=%d\nb=%d\nc=%d\nd=%d\n",a,b,c,d);

}

12.2位域(位段)

有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。

所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。

1.位域的定义和位域变量的说明

位域定义与结构定义相仿,其形式为:

struct位域结构名

{位域列表};

其中位域列表的形式为:

类型说明符位域名:位域长度

例如:

structbs

{

inta:8;

intb:2;

intc:6;

};

位域变量的说明与结构变量说明的方式相同。可采用先定义后说明,同时定义说明或者直接说明这三种方式。

例如:

structbs

{

inta:8;

intb:2;

intc:6;

}data;

说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。

对于位域的定义尚有以下几点说明:

1)一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。

例如:

structbs

{

unsigneda:4

unsigned:0/*空域*/

unsignedb:4/*从下一单元开始存放*/

unsignedc:4

}

在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。

2)由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。

3)位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:

structk

{

inta:1

int:2/*该2位不能使用*/

intb:3

intc:2

};

从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的。

2.位域的使用

位域的使用和结构成员的使用相同,其一般形式为:

位域变量名?位域名

位域允许用各种格式输出。

【例12.6】

main(){

structbs

{

unsigneda:1;

unsignedb:3;

unsignedc:4;

}bit,*pbit;

bit.a=1;

bit.b=7;

bit.c=15;

printf("%d,%d,%d\n",bit.a,bit.b,bit.c);

pbit=&bit;

pbit->a=0;

pbit->b&=3;

pbit->c|=1;

printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c);

}

上例程序中定义了位域结构bs,三个位域为a,b,c。说明了bs类型的变量bit和指向bs类型的指针变量pbit。这表示位域也是可以使用指针的。程序的9、10、11三行分别给三个位域赋值(应注意赋值不能超过该位域的允许范围)。程序第12行以整型量格式输出三个域的内容。第13行把位域变量bit的地址送给指针变量pbit。第14行用指针方式给位域a重新赋值,赋为0。第15行使用了复合的位运算符"&=",该行相当于:

pbit->b=pbit->b&3

位域b中原有值为7,与3作按位与运算的结果为3(111&011=011,十进制值为3)。同样,程序第16行中使用了复合位运算符"|=",相当于:

pbit->c=pbit->c|1

其结果为15。程序第17行用指针方式输出了这三个域的值。

12.3本章小结

1.位运算是C语言的一种特殊运算功能,它是以二进制位为单位进行运算的。位运算符只有逻辑运算和移位运算两类。位运算符可以与赋值符一起组成复合赋值符。如&=,|=,^=,>>=,<<=等。

2.利用位运算可以完成汇编语言的某些功能,如置位,位清零,移位等。还可进行数据的压缩存储和并行运算。

3.位域在本质上也是结构类型,不过它的成员按二进制位分配内存。其定义、说明及使用的方式都与结构相同。

4.位域提供了一种手段,使得可在高级语言中实现数据的压缩,节省了存储空间,同时也提高了程序的效率。

c语言 13

13文件

13.1C文件概述

13.2文件指针

13.3文件的打开与关闭

13.3.1文件的打开(fopen函数)

13.3.2文件关闭函数(fclose函数)

13.4文件的读写

13.4.1字符读写函数fgetc和fputc

13.4.2字符串读写函数fgets和fputs

13.4.3数据块读写函数fread和fwtrite

13.4.4格式化读写函数fscanf和fprintf

13.5文件的随机读写

13.5.1文件定位

13.5.2文件的随机读写

13.6文件检测函数

13.6.1文件结束检测函数feof函数

13.6.2读写文件出错检测函数

13.6.3文件出错标志和文件结束标志置0函数

13.7C库文件

13.8本章小结

13 文件

13.1 C文件概述

所谓“文件”是指一组相关数据的有序集合。这个数据集有一个名称,叫做文件名。实际上在前面的各章中我们已经多次使用了文件,例如源程序文件、目标文件、可执行文件、库文件 (头文件)等。

文件通常是驻留在外部介质(如磁盘等)上的,在使用时才调入内存中来。从不同的角度可对文件作不同的分类。从用户的角度看,文件可分为普通文件和设备文件两种。

普通文件是指驻留在磁盘或其它外部介质上的一个有序数据集,可以是源文件、目标文件、可执行程序;也可以是一组待输入处理的原始数据,或者是一组输出的结果。对于源文件、目标文件、可执行程序可以称作程序文件,对输入输出数据可称作数据文件。

设备文件是指与主机相联的各种外部设备,如显示器、打印机、键盘等。在操作系统中,把外部设备也看作是一个文件来进行管理,把它们的输入、输出等同于对磁盘文件的读和写。

通常把显示器定义为标准输出文件,一般情况下在屏幕上显示有关信息就是向标准输出文件输出。如前面经常使用的printf,putchar函数就是这类输出。

键盘通常被指定标准的输入文件,从键盘上输入就意味着从标准输入文件上输入数据。scanf,getchar函数就属于这类输入。

从文件编码的方式来看,文件可分为ASCII码文件和二进制码文件两种。ASCII文件也称为文本文件,这种文件在磁盘中存放时每个字符对应一个字节,用于存放对应的ASCII码。

例如,数5678的存储形式为:

ASCII码: 00110101 00110110 00110111 00111000

↓ ↓ ↓ ↓

十进制码: 5 6 7 8

共占用4个字节。

ASCII码文件可在屏幕上按字符显示,例如源程序文件就是ASCII文件,用DOS命令TYPE可显示文件的内容。由于是按字符显示,因此能读懂文件内容。

二进制文件是按二进制的编码方式来存放文件的。

例如, 数5678的存储形式为:

00010110 00101110

只占二个字节。二进制文件虽然也可在屏幕上显示,但其内容无法读懂。C系统在处理这些文件时,并不区分类型,都看成是字符流,按字节进行处理。

输入输出字符流的开始和结束只由程序控制而不受物理符号(如回车符)的控制。 因此也把这种文件称作“流式文件”。

本章讨论流式文件的打开、关闭、读、写、 定位等各种操作。

13.2 文件指针

在C语言中用一个指针变量指向一个文件,这个指针称为文件指针。通过文件指针就可对它所指的文件进行各种操作。

定义说明文件指针的一般形式为:

FILE *指针变量标识符;

其中FILE应为大写,它实际上是由系统定义的一个结构,该结构中含有文件名、文件状态和文件当前位置等信息。在编写源程序时不必关心FILE结构的细节。

例如:

FILE *fp;

表示fp是指向FILE结构的指针变量,通过fp即可找存放某个文件信息的结构变量,然后按结构变量提供的信息找到该文件,实施对文件的操作。习惯上也笼统地把fp称为指向一个文件的指针。

13.3 文件的打开与关闭

文件在进行读写操作之前要先打开,使用完毕要关闭。所谓打开文件,实际上是建立文件的各种有关信息,并使文件指针指向该文件,以便进行其它操作。关闭文件则断开指针与文件之间的联系,也就禁止再对该文件进行操作。

在C语言中,文件操作都是由库函数来完成的。在本章内将介绍主要的文件操作函数。

13.3.1 文件的打开(fopen函数)

fopen函数用来打开一个文件,其调用的一般形式为:

文件指针名=fopen(文件名,使用文件方式);

其中,

“文件指针名”必须是被说明为FILE 类型的指针变量;

“文件名”是被打开文件的文件名;

“使用文件方式”是指文件的类型和操作要求。

“文件名”是字符串常量或字符串数组。

例如:

FILE *fp;

fp=("file a","r");

其意义是在当前目录下打开文件file a,只允许进行“读”操作,并使fp指向该文件。

又如:

FILE *fphzk

fphzk=("c:\\hzk16","rb")

其意义是打开C驱动器磁盘的根目录下的文件hzk16,这是一个二进制文件,只允许按二进制方式进行读操作。两个反斜线“\\ ”中的第一个表示转义字符,第二个表示根目录。

使用文件的方式共有12种,下面给出了它们的符号和意义。

文件使用方式 意义

“rt” 只读打开一个文本文件,只允许读数据

“wt” 只写打开或建立一个文本文件,只允许写数据

“at” 追加打开一个文本文件,并在文件末尾写数据

“rb” 只读打开一个二进制文件,只允许读数据

“wb” 只写打开或建立一个二进制文件,只允许写数据

“ab” 追加打开一个二进制文件,并在文件末尾写数据

“rt+” 读写打开一个文本文件,允许读和写

“wt+” 读写打开或建立一个文本文件,允许读写

“at+” 读写打开一个文本文件,允许读,或在文件末追加数据

“rb+” 读写打开一个二进制文件,允许读和写

“wb+” 读写打开或建立一个二进制文件,允许读和写

“ab+” 读写打开一个二进制文件,允许读,或在文件末追加数据

对于文件使用方式有以下几点说明:

1) 文件使用方式由r,w,a,t,b,+六个字符拼成,各字符的含义是:

r(read): 读

w(write): 写

a(append): 追加

t(text): 文本文件,可省略不写

b(banary): 二进制文件

+: 读和写

2) 凡用“r”打开一个文件时,该文件必须已经存在,且只能从该文件读出。

3) 用“w”打开的文件只能向该文件写入。若打开的文件不存在,则以指定的文件名建立该文件,若打开的文件已经存在,则将该文件删去,重建一个新文件。

4) 若要向一个已存在的文件追加新的信息,只能用“a”方式打开文件。但此时该文件必须是存在的,否则将会出错。

5) 在打开一个文件时,如果出错,fopen将返回一个空指针值NULL。在程序中可以用这一信息来判别是否完成打开文件的工作,并作相应的处理。因此常用以下程序段打开文件:

6) if((fp=fopen("c:\\hzk16","rb")==NULL)

{

printf("\nerror on open c:\\hzk16 file!");

getch();

exit(1);

}

这段程序的意义是,如果返回的指针为空,表示不能打开C盘根目录下的hzk16文件,则给出提示信息“error on open c:\ hzk16 file!”,下一行getch()的功能是从键盘输入一个字符,但不在屏幕上显示。在这里,该行的作用是等待,只有当用户从键盘敲任一键时,程序才继续执行,因此用户可利用这个等待时间阅读出错提示。敲键后执行exit(1)退出程序。

7) 把一个文本文件读入内存时,要将ASCII码转换成二进制码,而把文件以文本方式写入磁盘时,也要把二进制码转换成ASCII码,因此文本文件的读写要花费较多的转换时间。对二进制文件的读写不存在这种转换。

8) 标准输入文件(键盘),标准输出文件(显示器),标准出错输出(出错信息)是由系统打开的,可直接使用。

13.3.2 文件关闭函数(fclose函数)

文件一旦使用完毕,应用关闭文件函数把文件关闭,以避免文件的数据丢失等错误。

fclose函数调用的一般形式是:

fclose(文件指针);

例如:

fclose(fp);

正常完成关闭文件操作时,fclose函数返回值为0。如返回非零值则表示有错误发生。

13.4 文件的读写

对文件的读和写是最常用的文件操作。在C语言中提供了多种文件读写的函数:

?字符读写函数 :fgetc和fputc

?字符串读写函数:fgets和fputs

?数据块读写函数:freed和fwrite

?格式化读写函数:fscanf和fprinf

下面分别予以介绍。使用以上函数都要求包含头文件stdio.h。

13.4.1 字符读写函数fgetc和fputc

字符读写函数是以字符(字节)为单位的读写函数。 每次可从文件读出或向文件写入一个字符。

1. 读字符函数fgetc

fgetc函数的功能是从指定的文件中读一个字符,函数调用的形式为:

字符变量=fgetc(文件指针);

例如:

ch=fgetc(fp);

其意义是从打开的文件fp中读取一个字符并送入ch中。

对于fgetc函数的使用有以下几点说明:

目录
设置
设置
阅读主题
字体风格
雅黑 宋体 楷书 卡通
字体大小
适中 偏大 超大
保存设置
恢复默认
手机
手机阅读
扫码获取链接,使用浏览器打开
书架同步,随时随地,手机阅读
首 页 < 上一章 章节列表 下一章 > 尾 页