函数是C语言的基石,函数库是面向过程编程语言的重要组成部分。 通常,使用库函数只需要理解其接口即可,接口和注释通常会较友好地透露如何使用的细节,而一些库函数手册还会增加一些有代表性的实例,以加强用户对其理解。但一些库函数在使用时出错频率较高,原因是实现时在安全、易使用性、效率等方面需要有所取舍,要避坑及深入理解其原因,自然需要深入理解库函数背后实现的细节。 1intscanf(charformat,ptrParaList)(stdio。h) Readfromstandardinputundercontroloftheformatstring;equivalenttofscanf(stdin,charformat,args) 全部参数为指针(第一个参数是字符指针); 读取整型或浮点型数据时会忽略掉前面的空白字符(以空白字符(isspace())为分隔); 2intfscanf(FILEf,charformat,ptrParaList)(stdio。h) Readfromthefileundercontroloftheformatstring。 当试图读取文件中的一行以空白字符分隔的字符串时,字符串并不以空白字符做分隔,而是将空白字符做为普通字符一起读取。如果以空白字符分隔分隔的字符串长度固定,可以指定固定长度读取,否则需要先fgets(),然后使用strtok()来处理。sscanf()也是如此。例如要读取下面的文本:PKL071ParkLens86。50BLJ372BallJoint1211。95PKL073ParkLens86。50FLT014OilFilter237。95DKP085DiscPads169。99GSF556GasFilter94。50FLT017OilFilter237。95 以下代码直接报错:typedefstructpartRecord{charpartNum〔PartNumSize1〕;charname〔MaxName1〕;intamtInStock;doubleunitPrice;}PartRecord;FILEftxt,fbin;PartRecordpart;if((ftxtfopen(parts。txt,r))NULL){printf(Cannotopenpartsfile);return1;}while(fscanf(ftxt,ssdf,part。partNum,part。name,part。amtInStock,part。unitPrice)4){printf(ssdf,part。partNum,part。name,part。amtInStock,part。unitPrice);if(fwrite(part,sizeof(PartRecord),1,fbin)!1){printf(Errorinwritingfile);return1;}} 以下代码没有正确读取:constintMAXCHAR128;charstr〔MAXCHAR〕{0};for(inti0;i7;i){fgets(str,MAXCHAR,ftxt);sscanf(str,ssdf,part。partNum,part。name,part。amtInStock,part。unitPrice);printf(ssdf,part。partNum,part。name,part。amtInStock,part。unitPrice);if(fwrite(part,sizeof(PartRecord),1,fbin)!1){printf(Errorinwritingfile);return1;}} 以下代码OK:constintMAXCHAR128;charstr〔MAXCHAR〕{0};for(inti0;i7;i){fgets(str,MAXCHAR,ftxt);sscanf(str,ssdf,part。partNum,part。name,part。amtInStock,part。unitPrice);strcpy(part。partNum,strtok(str,));strcpy(part。name,strtok(NULL,));part。amtInStockatoi(strtok(NULL,));part。unitPriceatof(strtok(NULL,));printf(ssdf,part。partNum,part。name,part。amtInStock,part。unitPrice);if(fwrite(part,sizeof(PartRecord),1,fbin)!1){printf(Errorinwritingfile);return1;}} 3charfgets(chars,intn,FILEf)(stdio。h) Readatmostn1charactersintothesarray;stopsatanewline,whichisincludedinthearray。Thearrayisautomaticallyterminatedwitha。charbuf〔6〕;fgets(buf,6,stdin);input:abcdeffputs(buf,stdout);output:abcde 4charstrcpy(chard,chars)(string。h) Copystringstostringd strcpy并没有边界检查,d所指缓冲区所有足够空间,否则会溢出。 5charstrcmp(chard,chars)(string。h) Comparestringdtostrings;return0ifds 注意两字符串比较相等的结果是0而不是其它值。 6charstrlen(chard)(string。h) Returnthelengthofstringd,notincludingtheterminatingNULL。 strlen函数的实现以‘为结束标志printf(d,strlen(s16end));3 7voidmemcpy(voidd,voids,intn)(string。h) Copyncharactersfromstod。 memcpy并没有检查d和s所指区域在复制时是否存在重叠情况,如果存在重叠,会出错。 memmove()有考虑重叠的情况。 8voidmemset(voidd,intc,sizetn);(string。h) Filldwithnoccurrenceofc。 注意其其参数类型,以下操作通常没有问题: 以单字节数据(字符或零值)填充字符串; 以布尔值填充布尔数组; 以零值填充数组; 以下操作通常不是预想的结果: 以非单字节数据填充数组; 以单字节数组(非零值)填充非字节数据的数组; 以整型数据1填充整形数组; 原因是memset的实现是以字节为单元去填充内存空间的:charmemset(dst,val,count)setscountbytesatdsttovalPurpose:Setsthefirstcountbytesofthememorystartingatdsttothecharactervalueval。Entry:voiddstpointertomemorytofillwithvalintvalvaluetoputindstbytessizetcountnumberofbytesofdsttofillExit:returnsdst,withfilledbytesvoidcdeclmemset(voiddst,intval,sizetcount){voidstartdst;while(count){(char)dst(char)val;dst(char)dst1;}return(start);} 9charstrtok(charstring,constcharcontrol)(string。h) 细节: 分割字符串时,需要多次调用以依次返回被分割后的子串,子串结尾会附以; 非第一次调用strtok()时,其首参以NULL开始,其内部实现以一个静态变量记录了下次开始分割的目标字符串的位置;strtok。ctokenizeastringwithgivendelimitersPurpose:definesstrtok()breaksstringintoseriesoftokenviarepeatedcalls。charstrtok(string,control)tokenizestringwithdelimiterincontrolPurpose:strtokconsidersthestringtoconsistofasequenceofzeroormoretexttokensseparatedbyspansofoneormorecontrolchars。thefirstcall,withstringspecified,returnsapointertothefirstcharofthefirsttoken,andwillwriteanullcharintostringimmediatelyfollowingthereturnedtoken。subsequentcallswithzeroforthefirstargument(string)willworkthruthestringuntilnotokensremain。thecontrolstringmaybedifferentfromcalltocall。whennotokensremaininstringaNULLpointerisreturned。rememberthecontrolcharswithabitmap,onebitperasciichar。thenullcharisalwaysacontrolchar。Entry:charstringstringtotokenize,orNULLtogetnexttokencharcontrolstringofcharacterstouseasdelimitersExit:returnspointertofirsttokeninstring,orifstringwasNULL,tonexttokenreturnsNULLwhennomoretokensremain。charstrtok(charstring,constcharcontrol){unsignedcharstr;constunsignedcharctrlcontrol;unsignedcharmap〔32〕;intcount;staticcharnextoken;for(count0;count32;count)map〔count〕0;Setbitsindelimitertabledo{map〔ctrl3〕(1(ctrl7));}while(ctrl);Initializestr。IfstringisNULL,setstrtothesavedpointer(i。e。,continuebreakingtokensoutofthestringfromthelaststrtokcall)if(string)strstring;elsestrnextoken;Findbeginningoftoken(skipoverleadingdelimiters)。Notethatthereisnotokeniffthisloopsetsstrtopointtotheterminalnull(str)while((map〔str3〕(1(str7)))str)str;stringstr;Findtheendofthetoken。Ifitisnottheendofthestring,putanullthere。for(;str;str)if(map〔str3〕(1(str7))){str;break;}Updatenextoken(orthecorrespondingfieldintheperthreaddatastructurenextokenstr;Determineifatokenhasbeenfound。if(stringstr)returnNULL;elsereturnstring;} 10isblank()andisspace()(ctype。h) intisblank(intc); Checkifcharacterisblank Checkswhethercisablankcharacter。 Ablankcharacterisaspacecharacterusedtoseparatewordswithinalineoftext。 ThestandardClocaleconsidersblankcharactersthetabcharacter()andthespacecharacter()。 Otherlocalesmayconsiderblankadifferentselectionofcharacters,buttheymustallalsobespacecharactersbyisspace。 intisspace(intc); Checkifcharacterisawhitespace。 Checkswhethercisawhitespacecharacter。 FortheClocale,whitespacecharactersareanyof: (0x20) space(SPC) (0x09) horizontaltab(TAB) (0x0a) newline(LF) v (0x0b) verticaltab(VT) f (0x0c) feed(FF) r (0x0d) carriagereturn(CR) Otherlocalesmayconsideradifferentselectionofcharactersaswhitespaces,butneveracharacterthatreturnstrueforisalnum。 11其它 11。1setbuf()用来设置缓冲区特性,如果需要改变缓冲的特点和大小等,使用该调用。 11。2fflush(stdin)、fflush(stdout)用来强制刷新缓冲区数据。如果需要在每次io操作前后,不希望缓冲中存在历史数据或者不期望的数据或者为了清除缓存等的时候使用。 11。3setbuf(stdin,NULL); 把输入缓冲区设置为无缓冲,直接从流读取数据。 11。4EOF的值是由fgetc()、fgets()动态设置,需要由此类函数调用后返回后的字符与EOF比较判断。 End