【文档说明】C语言课件第10章.ppt,共(87)页,209.001 KB,由小橙橙上传
转载请保留链接:https://www.ichengzhen.cn/view-44589.html
以下为本文档部分文字说明:
退出第10章预处理、输入/输出和文件操作C语言的预处理功能由C编译的预处理程序实现,负责分析和处理行首以“#”号开头的控制行。这些控制行包括宏替换、文件包含和条件编译等。C语言中并没有专门用于输入/输出的语句。为了给出方便的程序接口,C
语言中提供了“标准I/O库”,它是一组函数,可提供一系列I/O服务。10.1预处理功能10.2文件包含10.3条件编译10.4其他预处理功能10.5库函数使用方式10.6常用标准输入/输出函数10.7文件及有关操作10
.8其他一些常用的函数(宏)10.1预处理功能10.1.1简单宏定义和宏替换简单宏定义的一般形式是:#define标识符字符序列其中,标识符称作宏名,一般用大写字母。例如:#definePI3.1415926对于像PI这
样的宏在进行使用和处理时分为3个步骤:①在函数之外(往往在程序开头)使用#define控制行定义宏名;②在程序中使用已定义的宏名;③在程序进行编译时,由预处理程序对宏名进行宏替换,恢复被宏名所代替的字符序列的原貌。例如:#defineN50#definePI3.1415
926…inta[N];doubler,l;…l=2*PI*r;…经过宏替换之后,上述代码实际上就变成:…inta[50];doubler,l;…l=2*3.1415926*r;…有关宏的定义和使用要注意以下几点:①宏定义在源程序中要单独占用
一行,通常“#”号出现在一行的第一个字符的位置,允许在#号的前面有若干空格或制表符,但不允许有其他字符。②宏名用大、小写字母标识都行,为醒目起见,往往用大写字母表示。③用双引号括起来的标识符不做宏替换。例如:#defineTRUE1则打印语句printf("TRUE");会打印出TRUE,而
不是1。④宏定义可以嵌套。例如:#definePI3.1415926#defineTWOPI(2*PI)在预处理后,语句c1=TWOPI*r;便替换成c1=(2*3.1415926)*r;⑤宏替换只是按原来的定义“机械地
”进行替换,不管替换后的结果是对还是错。例如:#defineA3+2那么5/A被替换成5/3+2又如:#definePI3.l4l5q/*有错:4的前面是字母l,5的后面是字母q*/…area=PI*r*r;…照样进行宏替换:…area=3
.l4l5q*r*r;…为了避免出现上述第一种情况造成的误解,应当把字符序列用括号括起来。#defineA(3+2)⑥如果宏定义中的字符序列过长,在一行中放不下,可在该行末尾加续行符“\”,后随一个换行符
。#defineLONG_STRINGthisisaverylongstringthatisusedas\↙anexample⑦可以用宏定义来表示数据类型。#defineMAX100#defineENTRYstructentrytype在
程序中可用ENTRY定义变量:ENTRYtable[MAX],*p;⑧如果宏名出现在#号之后,则不对它进行替换。例如:#defineDEFdefine#DEFN10⑨宏名的有效范围是从定义它的地方开始到其所在文件末尾或者用#undef指令消除
该宏定义为止。宏名不受分程序结构的影响,在其有效范围内遵循宏替换的规则。10.1.2带参数的宏定义1.带参数宏定义的一般格式带参数的宏定义的一般形式是:#define宏名(参数表)字符序列例如,定义一个计算圆面积的宏:#
definePI3.1415926536#definearea(r)(PI*r*r)例10-2:下面的程序中使用了带参数的宏,它计算并输出给定半径的圆的面积。#definePI3.1415926536#def
inearea(r)(PI*r*r)voidmain(){printf("%f\n",area(6.18));}2.几点使用说明①使用宏定义时所带的实参可以是常量、已被赋值的变量名或者表达式。例10-3:将例10-2的程序改写为交互式输入半径
值的方式。#definePI3.1415926536#definearea(r)(PI*r*r)voidmain(){floatr;printf("Input:r=?\n");scanf("%f",&r);printf("Ar
ea=%f\n",area(r));}②带参数的宏替换与简单的宏替换在替换规则上是一致的,都是机械地进行替换,而不去理解用户的想法。例10-4:本例说明宏替换可能存在副作用。#definePI3.1415926536#defineare
a(r)(PI*r*r)voidmain(){printf("%f\n",area(4+6));}为了避免出现与常规理解上的不一致,建议对宏定义中字符序列的整体和各个参数都用圆括号括起来。例如,#definearea(
r)(PI*(r)*(r))那么,area(4+6)将被替换为:(3.1415926536*(4+6)*(4+6))③一个宏定义所带的参数可以多于一个,每个参数在宏替换中可以出现多次。#defineAVG5(a,b,c,d,e)(
((a)+(b)+(c)+(d)+(e))/5)④使用带参数的宏编写程序时,应注意:在宏定义中宏名和左圆括号之间没有空格。我们看一下如下宏定义:#definearea(r)(PI*(r)*(r))在area和“(”之间有空格。例如,x=area(5.6);替换后的结果为:x=(r)(3.14159
26536*(r)*(r))(5.6);3.带参数的宏和函数之间的比较带参数的宏定义与函数是不同的,不要把二者混淆起来。二者主要的区别有:宏定义中不存在参数类型问题,不仅宏名无类型,而且宏定义中的参数也无类
型。宏名和它的参数都只是一个符号代表,预处理时进行宏扩展。而带参数的宏在预处理阶段进行宏扩展。宏扩展只是简单地进行字符替换,并不对参数做任何计算、加工工作。始终应该记住:C预处理程序并不去理解用户编写的C程序,它只是机械地按照宏替换的定义把程序中的宏调用扩展为相应的字符序列,而不去“领会”其含义
。4.#、##运算符和宏替换嵌套在标准C语言中提供了两个仅用于宏替换的运算符:连串运算符#和连结运算符##。①在带参数的宏定义中,如果字符序列中的一个形参前面有“#”,那么在进行宏替换时,对应的实参就转换成用双引号括起来的字符串。例如:#definePR(x)printf(#x)…PR(data
undefined!);…将替换成…printf("dataundefine!");又如:#definestr(s)#s…p=str(helloworld);…将替换成:…p="helloworld"
;通常在实参中出现的转义字符在进行宏替换时,#运算符就在它的前面插入一个反斜线字符(\)。如果调用形式如下:PR("HelloWorld");则预处理时将它扩展为:printf("\"HelloWorld\"");②连结运算符##可以出现在上述两
种形式的宏定义中。当进行宏替换时,在替换字符序列中(不是在实参中)出现的##被删除,在##前面和后面出现的两个形参被相应的实参所替换,并且把它们合成一个单词。如果连结的结果构成宏名,则进一步执行宏替换处理。例如:#definede
bug(s,t)printf("x"#s"=%d,x"#t"=%s",x##s,x##t)…debug(1,2);经预处理后,被替换成:…printf("x""1""=%d,x""2""=%s",x1,x2);在字符串字面量联接之后,就成为下面形
式:printf(“x1=%d,x2=%s”,x1,x2);③带参数的宏替换也可以嵌套定义。例10-8:下面的程序展示宏的嵌套定义和替换,以及#运算符的应用。#include<stdio.h>#definePI(3.1415926536)#define
FUN(k)(k)*PI#definePR(format,value)printf(#value"=%"#format"\t",(value))#defineNLputchar('\n')#definePRINT1(f,x1)PR(f,x1);
NL#definePRINT2(f,x1,x2)PR(f,x1);PRINT1(f,x2)voidmain(){inti=99;floatarea;area=PI*6.8*6.8;PRINT1(d,i);PRINT2(f,FUN(2)*6.8,area);}程序运行结果如下:i=99(
2)*(3.1415926536)*6.8=42.725660area=145.26724210.2文件包含文件包含是C预处理程序的一种功能,它把指定文件的全部内容都包含到本文件之中。文件包含控制行的形式是:#include"文件名"例如:#include"defs.h"在#
include行中出现的文件名往往带有后缀.h,这种文件通常称为前导文件(或头部文件,header)。前导文件中除了可以包括宏定义外,也可以包括类型定义、全程变量和函数说明等。文件包含控制行还有另外的形式,即把双引号改
为尖括号:#include<文件名>这两种形式都可以使用,但还是有区别的:使用尖括号表明应在系统规定的“标准”目录中寻找指定的文件,如UNIX系统中就在/usr/include目录中去找,而不要在源文件所在的目录中查找;使用双引号表明应首先在正处理的源文件目录中搜索指定文件;如未找到
,则沿着提供给预处理程序的任选项(如-ls)所指示的查找路径搜索;如仍未找到,最后查找系统规定的“标准”目录。文件包含控制行可出现在源文件的任何地方,多数都放在靠近文件开头的地方。并且一个#include命令只能包含一个文件。文件包含是可以嵌套的。要注意,前导文件不能单独进行编译,它要附
属于某个C源文件一起编译。10.3条件编译条件编译控制行(又称条件蕴含)用来根据外部定义的条件去编译不同的程序部分。条件编译控制行有以下形式:#if常量表达式#ifdef标识符#ifndef标识符#else#elif常量表达式#endif#undef
标识符10.4其他预处理功能(1)行控制#line常数“文件名”(2)诊断控制#error字符序列(3)#pragma字符序列10.5库函数使用方式在调用库函数前,还要包含相应的前导文件,使用户程序和库函数能同时编译和连接。#include<stdio.h>intpr
intf(constchar*format,…);#include<math.h>doublepow(doublex,doubley);标准C语言中提供了15个前导文件,表10-1列出它们的名称和作用。10.6常用标准输入/输出函数标准I/O函数文件I/O函数标准I/O按其功能大
致可分为字符I/O函数、字符串I/O函数和格式化I/O函数。10.6.1getchar()和putchar()getchar()函数的语法格式是:#include<stdio.h>intgetchar(void);它不需要参数,其返回值是用户从终端上输
入的一个ASCII字符的值.。putchar()函数的语法格式是:#include<stdio.h>intputchar(intc);它的功能是发送一个字符(c中的字符)到标准输出(缺省说明情况下,就是用户终端)。例10-11:下面是
使用getchar()和putchar()函数的简单示例。/*echoinputtooutput*/#include<stdio.h>voidmain(){intc;for(;;){c=getchar();if(c!='!')putchar(c);elsebreak;}}1
0.6.2gets()和puts()gets()接受来自标准输入的一个字符串,puts()则发送一个字符串到标准输出。具有下述函数原型:char*gets(char*s);intputs(constchar*s);例10-12:读入用户输
入的字符串,并显示出各个字符相应的十进制编码值。当输入空串(同时按下Ctrl键和z键)时,程序终止。#include<stdio.h>voidmain(){charstring[80],*sp;printf("Whenprompted,typealine.\n");do{p
utchar(':');/*Thisisthepromptchar*/sp=gets(string);do{printf("%c=%d\n",*sp,*sp);}while(*sp++!='\0');/*Haltonnull
*/}while(*sp!='\0');/*Haltonnull*/}10.6.3printf()和scanf()1.printf()函数printf()的功能是按照指定的格式控制把相应参数值在标准输出上显示出来。其函数说
明格式是:#include<stdio.h>intprintf(constchar*format,arg1,arg2,…);format是指向字符串的指针,该字符串被称为转换控制字符串。转换控制串中包含两类字符:一类是普通字符,它们没有别的含义,只是照原样在屏幕(std
out文件)上输出。如:printf("Input:n=?\n");另一类字符是构成转换说明的字符,用来控制后面的参数进行转换,按要求的格式输出数据。如:printf("%d,%f,%f\n",a,val,rad);转换说明是由“%”开头,其后可顺序出现下列成分:0个或多个
标志、域宽说明、精度说明、长度修正符和转换类型字符。①标志字符共有5个,它们的表示和含义如表10-2所示。②域宽说明是十进制数字串,用来指示相应参数至少应使用多少个字符的宽度。当相应参数实际占位超过指定域宽时,就按实际宽度印出。当参数值未填满指定的区域时,就向
右对齐,左边用空格补充;如果有左对齐标志“-”,就向左对齐,右边用空格补充。③精度说明由小数点“.”和后面跟随的一个十进制数字串组成。对于整数形式(转换类型字符为d、i、o、u、x和X),它表示至少出现的数字个数;对于小数形式(转换类型字符为e、E和f),表示小数点后面数字的个数(小数
点前面至少有一个数字);对于字符串形式(转换类型字符为s),表示最多印出的字符个数;对于g和G,表示有效数字的上限。如果精度说明只有小数点,则精度为0。当精度说明和域宽说明有矛盾时,以精度说明为准。④长度修正符h和l用于有符号或无符号的整数形式,分别表示短型
量和长型量,而L用于小数形式,表示longdouble。⑤转换类型字符共十六个,用来说明对应参数的类型及输出格式,如表10-3所示。例10-14:printf()函数中有关精度说明的应用示例。#include<s
tdio.h>voidmain(){charc='A';charname[]="Liuhua";inti=1234;floatx=-123.456789;printf("|%10.2f|%10.2e|\n",x,x);printf("|%10.0f|%10.0e
|\n",x,x);printf("|%13.26f|\n",x);printf("|%40.32f|\n",x);printf("|%6.1c|%10.1d|\n",c,i);printf("|%8.
20s|%8.3s|%8.1s|%8.0s|\n",name,name,name,name);}运行结果如下:|-123.46|-1.23e+02||-123|-1e+02||-123.45678710937500000000000000||-123.45678710
937500000000000000000000||A|1234||Liuhua|Liu|L||2.scanf()函数scanf()函数的说明格式是:#include<stdio.h>intscanf(constchar*format,argp1,argp2,…);其中
转换控制串format由三类字符组成:①空白字符(空格、制表符、换行符)。此时继续读输入,直至读到下面的非空白字符(它不被读入)或者没有可读入的字符为止。②除“%”以外的普通字符。仅当输入字符流中出现与它们都匹配的字符串时,输入才能成
功;否则,只要有一个字符不匹配,输入就失败。③以“%”开头的转换说明。在转换说明中除开头的%外,其后可顺序出现如下成分:①可选的赋值抑制符“*”,表示跳过下面对应的输入域,不对该域内的数据进行转换和赋值。②可选的域宽说明,它是十进制整数,规定了输入对象的最大域
宽。③可选的长度修正符h、l或L,用于指定接收对象的大小。④转换类型字符。它是不可缺少的,而上面的3种成分均是任选的。scanf()的转换类型字符共14个,它们的含义如表10-4所示。10.7文件及有关操作10.7.1流和文件的概念C语言中支持两种形式的数据流,即:文本数据流
和二进制数据流。文本数据流是字符的有序序列,它由字符组成行,每一行由0个或多个字符再加上最后的换行符组成。二进制数据流抽象成一个线性字节序列。图10-2给出两种数据流从内存输出到磁盘文件上的差别。一般来说,文件是被命名的数据的集合体。操作系统是以
文件为单位对数据进行管理的。终端文件中有3个文件是特殊的,每个程序都要用到。这3个文件是:标准输入文件(stdin)—对应终端键盘、标准输出文件(stdout)—对应终端屏幕和标准出错信息文件(stderr)—也对应终端屏幕。这3个文件对所有运行的
程序来说,都是自动设置并且自动打开的。普通的磁盘文件又分为文本文件和二进制文件。文本文件又称为ASCII文件。一般文本文件与文本数据流相对应,二进制文件与二进制数据流相对应。每个磁盘文件都有一个名字。文件名是作为字符串存放的。10.7.2文件的打开与关闭1.打开文件所有文
件(除3个标准文件以外)在进行读写等操作之前都必须显式地打开,使用完之后应注意关闭。为了打开一个文件,要使用fopen()库函数。该函数的原型是:FILE*fopen(constchar*filename,constchar*mode);实参mode
指明打开文件的模式。mode控制该文件被打开后是用于读、写还是又读又写。mode可用的值如表10-5所示。fopen()的返回值是一个指向FILE结构的指针,以后对相应打开文件的操作就全部利用这个指针进行,而不再
用文件名。FILE是系统定义的结构类型名,用来记录控制一个数据流所需的各种信息,包括文件读写指针、缓冲区位置、出错标志和文件结束标志等。typedefstruct_iobuf{char*_ptr;/*nex
tcharacterposition*/int_cnt;/*numberofcharacterleft*/char*_base;/*locationofbuffer*/int_flag;/*modeoffileaccess*/int_fd;/*filedescriptor
*/}FILE;2.关闭文件函数fclose()关闭已打开的文件,切断文件指针fp与相应文件的联系。fclose()的函数原型是:intfclose(FILE*fp);例10-16:这个程序说明利用fopen()函数和不同的模式来打开指定的文件。#incl
ude<stdio.h>#include<stdlib.h>intmain(){FILE*fp;charch,filename[40],mode[5];while(1){printf("Enterafilename:");fgets
(filename,40,stdin);filename[strlen(filename)-1]=0;printf("Enteramode(max3characters):");fgets(mode,5,stdin);mode[strlen(mode)-1]=0;if((fp=fopen(
filename,mode))!=NULL){printf("Successfulopening%sinmode%s\n",filename,mode);fclose(fp);puts("Enterxtoexit,anyother
tocontinue.");if((ch=getc(stdin))=='x')break;elsecontinue;}else{fprintf(stderr,"Erroropeningfile%sinmode%s\n",file
name,mode);perror(filename);puts("Enterxtoexit,anyothertotryagain.");if((ch=getc(stdin))=='x')break;elsecontinue;}}retu
rn0;}10.7.3文件的读写写文件可以有三种方式:①使用格式化输出函数把文本数据保存到文件上。②使用字符输出函数把单个字符或整行字符保存到文件上。③使用直接输出函数把内存区中的内容直接保存到磁盘文件中。同样,从文件中读取数据也有三种方式:格式化输入、字
符输入和直接输入。1.格式化文件输出和输入(1)fprintf()函数它把输出数据发送到指定的文件中。其函数原型为:intfprintf(FILE*fp,char*fmt,…);(2)格式化文件输入利用fscanf()库函数可以从指定的文件中(而不是键
盘上)读取指定格式的数据。fscanf()函数的原型是:intfscanf(FILE*fp,constchar*fmt,…);例10-18:利用fscanf()从文件中读取格式化的数据。#include<std
lib.h>#include<stdio.h>voidmain(){FILE*fp;floatvalue[5];charfilename[20];printf("Inputthefilename.\n");scanf("%s",
filename);if((fp=fopen(filename,"r"))==NULL){fprintf(stderr,"Erroropeningfile.\n");exit(1);}fscanf(fp,"%f%
f%f%f%f",(value),(value+1),(value+2),(value+3),(value+4));printf("Thevaluesare%f,%f,%f,%f,%f\n",value[0],value[1],value[2],value
[3],value[4]);fclose(fp);}2.字符输入/输出(1)字符输入实现字符输入的函数有3个,它们是getc()、fgetc()和fgets()。前两个函数用来输入单个字符,第三个函数用来输入字符行。它们的
函数原型如下:intgetc(FILE*fp);intfgetc(FILE*fp);char*fgets(char*str,intn,FILE*fp);(2)字符输出用于字符输出的函数有两个:putc()和fputs()。pu
tc()函数把单个字符写到指定的文件中。其函数原型是:intputc(intch,FILE*fp);fputs()函数把一行字符写到文件中。fputs()的函数原型是:intfputs(constchar*str,FILE*fp);3.直接文件输入/输出直接文
件输入/输出的函数是fread()和fwrite()。它们用来向二进制数据流(文件)进行成批的输入/输出。①fread()的函数原型是:intfread(void*ptr,intsize,intcount,FILE*fp);②fwrite()的函数原型
是:intfwrite(constvoid*ptr,intsize,intcount,FILE*fp);10.7.4文件定位和出错检测1.文件定位每个打开的文件都有一个文件位置指针,它指示对相应文件进行读写操作的具体位置,所以也称作文件读写指针。读写位置总是表示
距文件开头的字节数。①fseek()函数可以按照需要把读写指针设置为文件中的任意位置,从而实现对文件的随机存取。fseek()的函数原型是:intfseek(FILE*fp,longintoffset,intwhence);对于二进制文件,新的文件位置是从whence所指示的基点开始加
上offset个字符。whence可以取以下三个值中的一个:SEEK_SET:从文件头开始计算,可用数字0代表。SEEK_CUR:从文件当前位置开始计算,可用1代表。SEEK_END:从文件尾开始计算,可用2代表。对于文本文
件,whence应该是SEEK_SET,offset应是0或者是先前调用ftell()函数时返回的值。fseek(fp,19000L,SEEK_SET);fseek(fp,50L,SEEK_CUR);fseek(fp,-100L,SEEK_END);②ftell()函数用来给
出流式文件中读写指针的当前值。其函数原型为:longintftell(FILE*fp);③rewind()函数的功能是把文件的读写指针重新设置为该文件的开头。它的原型是:voidrewind(FILE*fp);其作用等价于:(voi
d)fseek(fp,0L,SEEK_SET)2.文件操作出错检测①ferror()函数用来确定文件操作中是否出错。它的函数原型为:intferror(FILE*fp);②clearerr()函数的作用是清除文件结束标志和文件出错标志(置为0)。其函数原型为:
voidclearerr(FILE*fp);③feof()函数用来检测指定文件是否读写到文件末尾。仅当文件结束标志被设置了,函数feof()才返回一个非0值。它的函数原型是:intfeof(FILE*fp);10.8其他一些常用的函数(宏)(1)ctype.h文
件中字符测试的函数(宏)下列函数(宏)使用之前要在程序开头写上文件包含行:#include<ctype.h>intisalpha(intc);测试c是否为a~z或A~Z间的字母,若成功,返回非0值。inti
supper(intc);测试c是否为大写字母(即A~Z),若成功,返回非0值。intislower(intc);测试c是否为小写字母(a~z),若成功,返回非0值。intisdigit(intc);
测试c是否为数字:0~9,若成功,返回非0值。intisxdigit(intc);测试c是否为十六进制数字:0~9和a~f或A~F,若成功,返回非0值。intisalnum(intc);测试c是否为字母或数字,若成功,返
回非0值。intisspace(intc);测试c是否为空白符、制表符、换行符、垂直制表符、换页符、回车符,若成功,返回非0值。intispunct(intc);测试c是否为标点符号,即:除字母、数字、空格以外的可打印字符,若成功,返回非0值。intisprin
t(intc);测试c是否为可印出字符,即任何图形字符,若成功,返回非0值。intiscntrl(intc);测试c是否为控制字符,即:0<=c<040||c==0177。若成功,返回非0值。(2)exit()和system()
exit()和system()函数都包含在stdlib.h文件中,所以,在使用它们之前应在程序开头写上:#include<stdlib.h>exit()的函数原型是:voidexit(intstatus);system()的函数原型是:intsystem(cons
tchar*string);它的作用是把string所指向的字符串传给用户当前使用的命令处理程序,由该命令处理程序执行参数所指出的命令,并等待它的完成。如在UNIX环境下执行语句:system("cattest.c");(3)几个最常用的函数在string.h中有许多对字符串进行比较、复制
、加工的函数,下面是几个最常用的函数。①strcat()的功能是连接两个字符串。strcat()的函数原型是:char*strcat(char*string1,constchar*string2);②strcmp()的功能是比较两个
字符串。它的函数原型是:intstrcmp(constchar*string1,constchar*string2);③strcpy()的功能是把一个字符串复制到另一个字符数组中。C语言并没有提供字符串
赋值的操作符,但是,可利用函数strcpy()来实现这一功能。strcpy()的函数原型是:char*strcpy(char*string1,constchar*string2);④strlen()的功能是统计一个字符串的长度。其函数原型为:intstrl
en(constchar*string);其返回值是参数所指向的字符串中字符的个数(不包括结尾的空字符)。例10-26:写一个程序,实现最简单的散列查表算法。散列(hash)算法是把输入的名字转换成散列值,然后
用这个值作为查表的索引。求散列值的方法很多,这里采用最简单的一种:把字符串中的字符编码值相加,除以数组的长度,其余数就是该名字的散列值。#include<string.h>#include<stdio.h>#defineHASHSIZE10typedefstru
ctnlist{char*name;intcount;structnlist*next;}NLIST;NLIST*hashtab[HASHSIZE];inthash(char*s);NLIST*lookup(char*s);char*strsave(
char*s);NLIST*install(char*name);voidmain(){inti;charstr[20];NLIST*p;printf("Input5names.\n");for(i=0;i<5;i++){scanf
("%s",str);p=install(str);printf("p->name=%s\tp->count=%d\n",p->name,p->count);}}inthash(char*s)/*formhashvalueforstrings*
/{inthashval;for(hashval=0;*s!=’\0’;)hashval+=*s++;hashval%=HASHSIZE;printf("***%d\n",hashval);return(hashval);}NLIST*lo
okup(char*s)/*lookforsinhashtab*/{NLIST*np;for(np=hashtab[hash(s)];np!=NULL;np=np->next)if(strcmp(s,np->name)==0)return
(np);/*foundit*/return(NULL);/*notfoundit*/}char*strsave(char*s)/*savesintoaspaceallocated*/{char*p;if((p=(char*)calloc(strlen(s)+
1,1))!=NULL)strcpy(p,s);return(p);}NLIST*install(char*name)/*createanewitemorincreaseitscount*/{NLIST*np;int
hashval;if((np=lookup(name))==NULL){/*createanewitem*/np=(NLIST*)calloc(1,sizeof(NLIST));if(np==NULL)return(NULL);if((np->name
=strsave(name))==NULL)return(NULL);hashval=hash(np->name);np->next=hashtab[hashval];hashtab[hashval]=np;np->count=1;}else/*increasethecount*/np
->count++;return(np);}