c语言学习(四)

来源:互联网 发布:怎样才算掌握c语言 编辑:程序博客网 时间:2024/05/03 00:42

30typedef

C语言支持用typedef声明新的类型名来代替已有的类型名。如

typedefint  INTEGER;

INTEGER i=0;//等同于inti=0;

又如:

typedef struct

{

       int year;int month;int day;

}DATE;//声明一个结构体,命名为DATE

DATEbirthday;//声明一个结构体变量

typedef int  ARR_10[10];//声明ARR_10为长度为10的数组类型

ARR_10 a,b,c;//a、b、c都是长度为10的整型数组。方便定义多个等长度的数组。

typedefint  (* POINTER)();//声明一个指向函数的指针,类型名为POINTER

POINTER p1,p2;//

【typedef 数据类型 新类型名】

定义一个新类型步骤为(以ARR_10为例):

①按原数据类型定义(int a[10];)

②将变量名(a)改为新类型名(变成int  ARR_10[10])

③在最前面加上typedef(变成typedefint  ARR_10[10])

④使用新类型名声明变量(ARR_10 a,b,c;)

typedef与#define看似相同,但本质不同。#define是在预编译时处理的,只是进行简单的字符串替换。而typedef是在编译时处理的,也不是进行字符串替换,而是真正用新类型去定义变量。

当不同源文件用到同一类数据类型(尤其是数组、结构体、指针等),将新类型名typedef在头文件中,可以在多个源文件中#include使用。

typedef还可以解决由不同编译系统造成的数据长度不同问题。如int数据可能是2个字节或4个字节。将一个以4字节处理的程序移植到2字节的系统中,通常要把int改为long。而使用typedefint  INTEGER;用INTEGER定义数据,移植时改成typedeflong INTEGER;就只需改动一句话。

31、位运算

       位运算符只能用于整型或字符型数据的操作,不能是实型数据。

运算符

作用

按位与&

①某些位置0(跟一个对应位为0其他位为1的数按位与)

②取出指定位上的二进制数(与指定位上为1的数与,通常结合移位运算)

按位或|

①某些位置1(跟一个对应位为1其他位为0的数按位或)

 

 

 

按位异或^

①按位翻转(与1异或相当于取反,在对应位上与1异或,对应位取反)

②与0异或保留原值

③交换整型或字符型数据,不需要临时变量

(a=a^b;

b=b^a;

a=a^b;)

④一个数与自己异或为0

取反~

01,10

左移<<

数据左移,右边补0。在高位没有溢出的情况,<<2相当于*2

 

右移>>

对于无符号的数,右移高位补0。对于有符号的数,原符号位为0(正数)则高位补0;若原符号位为1(负数),则取决于系统,有的补0,有的补1。(vs2010负数右移高位补1

 

不同长度的数据参与位运算(long a与int b),则短的数据高位补0或1——若b为正数则高位补0;若b为负数高位补1。

32、位段(bit field

       在一些情况下,存储信息只需一位或几位即可,不需要一个或多个字节。如“真/假”用“1/0”表示只需一位,用一个int表示浪费了空间。此时可以用位段(或称位域)表示若干位。

       位段是C中允许一个结构体中以位为单位来指定成员所占的长度。如

       struct bitfield

{

       unsignedint a: 3;

       unsignedint b: 4;

       unsignedint c: 5;

       int d: 5;

}data;

a、b、c分别占3、4、5个bit。d是普通的整型变量。引用和赋值与普通成员相同data.a=7;a只占3位,所以它可表示的最大值为7。若data.a=8;则超过表示范围,只取低3位,则a为0。

       对于上面的结构体,内存占用如下(以unsigned int占4字节的vs2010为例):

a(3b)

b(4b)

d(5b)

剩余20bit

d(占4个字节)

位段使用说明:

       ①位段成员的类型只能是unsigned或者int整型。不管结构体中有几个字段,结构体至少占用一个unsigned的空间(4字节)。比如上面的data只有abc三个字段,共12bit,但sizeof(data)是4,即占用了4个字节。在abc之后的20个字节空着。

       ②可以有未命名的字段:

       unsignedint a: 3;

       unsignedint b: 4;

       unsignedint : 5;//跳过5bit

       unsignedint d: 4;

       unsignedint  : 0;//从下一个存储单元开始存放e

       unsignedint e: 6;

d之前有一个未命名的长度为5的字段,表示这部分字段空中不用。即d跳过5bit开始分配空间。e之前有一个长度是0的未命名字段,表示e从下一个unsigned数据开始分配6bit

       一个字段不能跨越两个unsigned(不能跨越两个存储单元),如果第一个unsigned的空间不足,则从下一个unsigned开始分配。如

       unsignedint d: 6;//

       unsignedint e: 27;//

ed所在的unsigned中(剩余26bit)不够分配,所以要跳至下一个unsigned,故占用两个unsigned,共8字节。

       字段长度不能超过unsigned int的长度32bit),否则编译出错。

       ⑤字段可以用整型格式输出(”%d” “%o”等)

       ⑥字段可以参与数值表达式,如data.a+5/data.b

       ⑦不同系统对字段的分配顺序不同。对同一个字节,有的可能从左至右分配字段,有的可能从右至左分配。并且系统分的不同,②③④点中的存储单元长度也有所不同。所以字段的使用移植性差,用在特定的系统中。

33、文件

       文件是存储在外部介质上数据的集合。操作系统是以文件为单位对数据进行管理的。对操作系统而言每一个与主机相联的输入输出设备(键盘、显示器等)都是文件。数据通常保存在磁盘文件中。

       ØASCII文件与二进制文件

       根据数据的组织形式可分为ASCII文件和二进制文件ASCII文件又称文本文件,它的每一个字节放一个ASCII代码,代表一个字符。二进制文件把数据在内存中存放的二进制形式原样输出到磁盘上。例如一个整数10000,在内存中占两个字节,用ASCII文件保存则要用5个字节(10000);而用二进制文件只需两个字节(int长度)。因此ASCII文件便于逐个处理,也便于输出,但占用较大空间。二进制文件可以节省外村空间和转换时间但一个字节并不对应一个字符,不能直接输出。一般采用二进制存放数据。

       Ø缓冲文件系统与非缓冲文件系统

       缓冲文件系统是指系统自动的在内存区为每一个正在使用的文件开辟一个缓冲区。从内存向磁盘输出数据必须先送到缓冲区,装满缓冲区后才送到磁盘。从磁盘读入数据也是一样。

       非缓冲文件系统是指系统不自动开辟确定大小的缓冲区,而由程序为每个文件设定缓冲区。

       ANSI C标准采用缓冲文件系统。即用缓冲文件系统处理文本文件和二进制文件。

       Ø文件类型指针

       每个被使用的文件都在内存中开辟一个区,用来存放文件的有关信息(如文件的名字、文件状态及文件当前位置等)。这些信息保存在一个由系统定义的结构体变量中,该结构体名为FILE

       typedefstruct

       {

              shortlevel;//缓冲区“满”或“空”的程度

              unsignedflags;//文件状态标志

              charfd;//文件描述符

              unsignedchar hold;//如无缓冲区不读取字符

              shortbsize;//缓冲区大小

              unsignedchar *buffer;//数据缓冲区的位置

              unsignedar *curp;//指针,指向当前的指向

              unsignedistemp;//临时文件指示器

              shorttoken;//用于有效性检测

       }FILE;

       通过定义FILE结构体变量来操作文件。如FILE *fp;有多个文件时可以定义FILE类型数组FILE f[5];

       Ø文件的打开与关闭

       1)文件的打开——fopen函数

       FILE*fp=fopen(char*filename,char *mode);//指定文件名和打开方式。

       fp=fopen(“./file1”,”w”);//用只写的方式打开file1文件。

       fopen函数返回指向file1文件的指针,这样fp就可以操作file1文件了。filename可以是字符串常量,也可以是变量,甚至是scanf输入的字符串变量。不同的打开方式说明如下:

      

文件打开方式

含义

“r”(只读)

为输入打开一个文本文件

“w”(只写)

为输出打开一个文本文件

“a”(追加)

向文本文件尾部追加数据

“rb”(二进制文件只读)

为输入打开一个二进制文件

“wb”二进制文件只写)

为输出打开一个二进制文件

“ab”(二进制文件追加)

向二进制文件尾部追加数据

“r+”(读写)

为读写打开一个文本文件

“w+”(读写)

为读写打开一个文本文件

“a+”(读写)

为读写打开一个文本文件

“rb+” (读写)

为读写打开一个二进制文件

“wb+” (读写)

为读写打开一个二进制文件

“ab+” (读写)

为读写打开一个二进制文件

 

(输入输出是相对程序而言的)

打开方式说明:

       ①用“r”或“rb”打开的文件只能读取数据,不能写入数据。并且要求这个文件事先存在,否则出错。

       ②用“w”或“wb”打开一个文件,只能写入数据不能读取。若这个文件不存在则会先创建文件。若这个文件已存在则会删除原文件重新创建

       ③用“a”或“ab”打开的文件事先必须存在,打开时位置指针移到文件末尾。原文件不会被删除。

④使用fopen打开一个文件可能会出错。出错原因可能是用“r”打开一个不存在的文件;磁盘出错;磁盘已满等。此时fopen返回空指针NULL。所以常用以下方式打开:

if((fp=fopen(“file”,”r”))==NULL)

{

       printf(“cannot open this file\n”);

       exit(0);

}

exit(0);函数作用是关闭所有文件,终止正在执行的程序。定义在stdlib头文件中。

       ⑤在程序开始运行时,系统自动打开3个标准文件:标准输入、标准输出、标准出错输出。这三个文件与终端(键盘、显示器)相关联,所以在从终端输入或输出都不需要打开终端文件——上面提到任何与计算机相联的终端设为都是文件。系统自定义了三个文件指针stdinstdoutstderr指向3个标准文件。

例如putchar(charc)函数其实在stdio中的宏定义:

#define putchar(c)fputc(c,stdout);

即将字符c写到stdout文件中,stdout与输出终端(显示器、打印机)相联,由stdout为输出终端文件提供数据源。

       (2)文件的关闭——fclose函数

       fclose(FILE *fp);//关闭指定文件

fclose使文件指针变量不指向该文件,也就不能通过该指针操作文件。

       在程序终止之前应关闭文件。因为在向文件写数据时,是先放在缓冲区,缓冲区满后才输出给文件。如果数据未充满缓冲区而程序终止,就会将缓冲区的数据丢失。用fclose关闭则会在先把缓冲区数据写入磁盘再释放指针变量。

       Ø文件读写

       1fputcputc)与fgetcgetc

       fputc(char c,FILE*fp)

fputc函数将一个字符c写入到fp指向的文件中。若输出成功返回c字符,若失败返回EOF(即-1,定义在stdio中)。

       char ch=fgetc(fp);fgetc函数从fp指向的文件中读取一个字符。在执行fgetc函数读字符时遇到文件结束符函数返回一个文件结束标志EOF,故可以通过判断fgetc返回值是否为EOF判断文件是否读取结束。

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

              putchar(ch);

       上述方法是针对文本文件读取的,对于二进制文件读入的数据可能是-1,故不能用判断是否EOF的方法读取二进制文件。

       用feof(FILE*fp)函数来判断fp指向的文件当前状态是否为“文件结束”,若文件结束feof返回非0值,否则返回0。

       feof是通过判断FILE结构体变量中的flag是否为结尾标志来返回值的。而用输入函数读取数据时,通过文件位置指针(fp->_ptr)来获取当前字符。只有当文件位置指针(fp>_ptr)到了文件末尾,然后再发生读/写操作时,标志位(fp->_flag)才会被置为含有_IOEOF。然后再调用feof(),才会得到文件结束的信息。所以用feof判断是否到文件尾很可能会多读取一次数据,而多的数据通常是乱码。运行下面的程序:

       while(!feof(fp)){

              c=fgetc(fp);

              putchar(c);

       }

会多输出一个字符(对应c的值为-1)。原因就是在读完最后一个字符后,fp->flag仍然没有被置为_IOEOF,因而feof()仍然没有探测到文件结尾。直到再次调用fgetc()执行读操作,feof()才能探测到文件结尾。解决方法是先读取一次(下面的fread也是一样):

       c = fgetc(fp);

while(!feof(fp))

{

putchar(c);

c = fgetc(fp);

}

       2fwritefread

       fputcfgetc用来读写一个字符,而fwritefread可以读取一组数据(如一个实数,一个结构体变量等)。fwritefread通过读取一个数据块的方式实现。

       unsignedfwrite(源数据指针,源数据大小,源数据数量,文件指针)

fwrite(char *ptr,unsigned size,unsigned n,FILE *fp)//把ptr指向的n*size个字节输出到fp指向的文件中。

       size是源数据大小,可以是一个基本数据(int、float),可以是一个结构体变量(占不定多个字节),n是要输出size大小数据的个数,所以总共是n*size个字节。

       int arr[10];

       fwrite(arr,sizeof(int),10,fp);//把10个整型数据输出到fp指向文件

又如:struct student

         { char name[10]; int num; int age; chardept[10]; }stu[4];

       fwrite(&stu[i],sizeof(student),1,fp);//把结构体数组中的结构体变量输出到fp——由于字节对齐,sizeof(student)不是28而是32。

       unsignedfread (目的数据指针,数据大小,数据数量,文件指针)

fread(char *pt,unsigned size,unsigned n,FILE *fp)//从fp指向文件中读取n个长度为size的数据项,让去pt指向的内存区。

       fwritefread若执行成功则返回n,即操作的数据项个数。

       fwritefread适用于二进制文件读写。不同存储格式的文件最好用对应的方式打开文件,否则可能出现转化错误——二进制文件是与内存中相同原样存储,不进行转换。而ASCII文件会对回车换行在输入输出时转换。

       同样可以用feof判断是否到文件尾。

       3fprintffscanf

       printfscanf相同,fprintffscanf也用于格式化输出,只是fprintffscanf针对文件,printfscanf针对终端。

       fprintf(文件指针,格式字符串,输出列表)

       fscanf(文件指针,格式字符串,输入列表)

       inti=1;float f=3.14;

       fprintf(fp,“%d,%f”,i,f);//if“%d,%f”格式输出到fp。在文件中会有“1,3.14”

       fscanf(fp,“%d,%f”,&i,&f);//fp中按“%d,%f”格式输入两个数据,赋给if——文件中数据的格式必须是侏诸如“3,4.5”,即需要中间的“,”。

       4)其他读写函数

       多大C编译系统都提供putw/getwfputs/fgets等函数。

       putw/getw读写一个字(整数):

       putw(int w,FILE*fp)将整数w(即一个字)输出到fp指向的文件

       getw(FILE *fp)从fp读取一个整数,返回这个整数

       fputs/fgets读写一个字符串:

       fputs(char *s,FILE*fp)将字符串s输出到fps可以是常量或者数组、指针。’\0’不输出。

       fgets(char*s,n,FILE *fp)fp获得n-1个字符赋给s指向地址,并在最后添加’\0’,共组成n个字符。若读取时遇到回车或EOF,读入即结束。

       5)自定义读写函数

       可根据fputcfgetc读取一个字节的功能,来定义一次读取任意个字符的函数。如

       void putfloat(float num,FILE *fp)

{

              char *s;

              int i;

              s=(char * )&num;

              for(i=0;i<sizeof(float);i++)

                     fputc(*(s+i),fp);

}

上面定义了一个一次输出sizeof(float)个字节的函数,用以输出float数据。同理可用fgetc实现getfloat(FILE *fp)。——用一个char型指针依次指向float的各个字节。

       Ø文件定位

       文件中有一个位置指针(vs2010下是fp->_ptr),指向当前读写的位置。通过改变这个指针的位置,实现随机读写(而不是从头到尾的顺序读写)。

       rewind函数

       rewind(FILE *fp)实现将位置指针返回文件头(第一个字符所在的位置)。可以不用重新fopen

       fseek函数

       fseek(FILE*fp,long offset,int base)fp指向的文件的位置指针移动到以base为基准、以offset为偏移量的位置。base可以是以下的值:

base

变量名

变量值

文件头

SEEK_SET

0

当前位置

SEEK_CUR

1

文件尾

SEEK_END

2

base基准上向后移动offset字节。若offset是负数则向前移。

       fseek(fp,10L,0);//距文件头10字节处

       fseek(fp,-5L,SEEK_END);//从文件尾向前移动5字节

文件尾是指最后一个字符的下一个位置。在使用feof特别要注意。

       ftell函数

       ftell(FILE *fp)返回当前文件指针所在位置。若出错返回-1

       Ø出错检测

在对文件调用输入输出函数后,输入输出函数本身的返回值可以判断是否出错,也可以用ferror判断。

       ferror(FILE *fp):在对文件调用输入输出函数后,调用ferror,若返回非0表示出错。对同一个文件每调用一次输入输出函数都会产生一个新的ferror函数值,因为应当在调用一个输入输出函数后立即检查ferror的值,否则信息对丢失。

       clearerr(FILE*fp)函数清除文件错误标志和文件结束标志置0。只要出现错误标志就一直保留,直到对同一个文件调用clearerr函数或rewind函数,或任何其他一个输入输出函数。

值得注意的是,文件句柄在作为入参传入时,它并不复制一个形参,而是将文件句柄传入,所以在调用函数后,文件指针就发生了改变。这样一来可以连续调用读写函数来对同一个文件读写。

0 0
原创粉丝点击