| 開頭將文章中代碼所在的環(huán)境介紹下: [Mac-10.7.1 Lion Intel-based] 
 Q: 看到stdio.h中有這么多關(guān)于輸入或者輸出的函數(shù),怎么會這么多? A: 其實(shí)基本的函數(shù)不多,不過從易于上層使用的角度,又被封裝出很多特定情況的函數(shù)接口,所以才顯得很多。 
 Q: 比如關(guān)于字符操作的函數(shù)有g(shù)etc, fgetc, getch, getche還有g(shù)etchar,它們有什么區(qū)別嗎? A: 一個重要的區(qū)別是getc, fgetc, getchar是標(biāo)準(zhǔn)函數(shù),而getch和getche不是標(biāo)準(zhǔn)規(guī)定的。不是標(biāo)準(zhǔn),就意味著它的實(shí)現(xiàn)可能在不同平臺有不一樣的表現(xiàn)。正如getch一樣,windows平臺被包含在conio.h頭文件中,運(yùn)行到getch,可能需要等待輸入,當(dāng)有輸入時就會運(yùn)行過去不需要再按回車; getche在windows下和getch類似,不過多了最后的字母e,它表示echo,即在輸入后會回顯輸入的字符。不過它們在mac平臺的表現(xiàn)卻不大一樣,這里不做具體介紹。 現(xiàn)在介紹下getc, fgetc和getchar.用man getc得到如下信息: 
 
 可以發(fā)現(xiàn),getc基本等同于fgetc,getchar是getc的特殊形式,是從標(biāo)準(zhǔn)輸入獲取字符。 
 Q: getch和getche接收了輸入即可繼續(xù)運(yùn)行,而getchar函數(shù)當(dāng)緩沖區(qū)中沒有數(shù)據(jù)時,需要輸入字符最終回車才能繼續(xù)執(zhí)行,這是不是意味著getch和getche是不帶緩沖的,而getc, fgetc和getchar是帶緩沖的? A: 是的。按照標(biāo)準(zhǔn)來說,getc和fgetc在不涉及交互的時候是全緩沖模式(在緩沖區(qū)大小滿后提交數(shù)據(jù)),如果是涉及到交互(如標(biāo)準(zhǔn)輸入),那么是行緩沖;而getchar就行緩沖模式(回車會提交輸入緩沖區(qū)數(shù)據(jù),不過如果緩沖區(qū)已經(jīng)有數(shù)據(jù)了就不需要等待回車了). 
 Q: 既然fgetc是接收輸入的字符,返回值用char或者unsigned char不就行了,為什么用int呢? A: 這個主要是因?yàn)槲募Y(jié)束或者讀寫文件出錯的標(biāo)志被規(guī)定成EOF,也就是-1導(dǎo)致的。unsigned char根本取不到-1這個值,而如果用char做返回值的話,它無法分辨0xFF字符和EOF,因?yàn)檫@兩個數(shù)值都被char認(rèn)為是-1,所以它也不能作為返回值。 舉個例子吧: 使用如下代碼向一個文件中只寫入0xFF字符,然后保存: #include <stdio.h>    int main (int argc, const char * argv[])  {        FILE *fp = fopen("test", "w");      if(fp)      {          fputc(0xFF, fp);          fclose(fp);      }            return 0;  }  
運(yùn)行完,用hexdump確認(rèn)此文件中的數(shù)據(jù)為0xFF:
 
 
 然后使用如下代碼讀取test文件中的數(shù)據(jù),并打印出來: #include <stdio.h>    int main (int argc, const char * argv[])  {        FILE *fp = fopen("test", "r");      if(fp)      {          char ch;            printf("open test ok\n");          while((ch = fgetc(fp)) != EOF)          {              printf("%d", ch);          }            printf("read test end\n");          fclose(fp);      }            return 0;  }  
運(yùn)行后可以發(fā)現(xiàn)輸出結(jié)果如下,這就意味著fgetc執(zhí)行是遇到0xFF時被當(dāng)做了EOF而導(dǎo)致結(jié)束了。
 
 
 Q: 上面提到getchar等同于getc(stdin), stdin到底是什么? A: [Mac-10.7.1 Lion Intel-based] stdio頭文件中: #define stdin   __stdinp  #define stdout  __stdoutp  #define stderr  __stderrp  
extern FILE *__stdinp;  extern FILE *__stdoutp;  extern FILE *__stderrp;  
可以看到,它們只是FILE *類型的一個變量,對于任何一個應(yīng)用程序,使用c運(yùn)行時庫都會默認(rèn)自動為應(yīng)用程序創(chuàng)建這3個文件句柄。
 
 
 Q: 還有關(guān)于字符串輸入的gets函數(shù),聽說使用它進(jìn)行輸入有風(fēng)險的,如何表現(xiàn)的? A: 因?yàn)樗鼪]有對于輸入數(shù)據(jù)大小進(jìn)行確定,也就是可能導(dǎo)致輸入的數(shù)據(jù)過多覆蓋了不該覆蓋的數(shù)據(jù)導(dǎo)致出問題。 舉個例子: #include <stdio.h>    #define PRINT_CH(ch)    printf(#ch" is %c\n", (ch));  #define PRINT_STR(str)  printf(#str" is %s\n", (str));    int main (int argc, const char * argv[])  {        char ch = 'a';      char buf[4];      char *ret = gets(buf);      if(ret != NULL)      {          PRINT_STR(ret)      }      PRINT_CH(ch)            return 0;  }  
運(yùn)行:
 如果輸入3個字符hel后回車,得到的結(jié)果是正常的: 
 
 如果輸入了4個字符hell后回車,結(jié)果如下: 
 可以看到ch字符的數(shù)據(jù)已經(jīng)不正常了; 
 如果輸入5個字符hello后回車,結(jié)果如下: 
 可以看到ch字符已經(jīng)被覆蓋為hello中的o了。這和剛剛輸入4個字符的方式都是發(fā)生了緩沖區(qū)溢出,也就是超過了緩沖區(qū)buf的大小,覆蓋了其它數(shù)據(jù)。 至于ch為什么是字符o, 這需要明白:ch和buf都被保存在棧中,且ch和buf相鄰,如果棧是從高到低,那么ch保存在高地址,數(shù)據(jù)hello將前4個字節(jié)保存在buf中,最后一個o就被保存在了ch所在的地址區(qū)域里面。 
 Q: 既然這樣,那么使用什么可以更好地避免緩沖區(qū)溢出呢? A: 可以使用fgets函數(shù),它的一個參數(shù)就是緩沖區(qū)大小。 char *fgets(char * restrict str, int size, FILE * restrict stream);  
如果讀取成功,函數(shù)返回讀取字符串的指針;
 如果開始讀取直接遇到文件結(jié)尾,返回NULL,緩沖區(qū)數(shù)據(jù)和原來保持一致; 如果讀取出錯,返回NULL, 緩沖區(qū)數(shù)據(jù)不確定; 
 Q: fgets如果返回NULL,可能是直接遇到文件結(jié)束或者獲取出錯,怎么區(qū)分呢? A: 這就需要如下兩個函數(shù)了:feof和ferror. int  ferror(FILE *stream);  
如果遇到文件尾,那么feof返回非0的數(shù)值,否則返回0.
 
 如果遇到文件操作錯誤,那么ferror返回非0的數(shù)值,否則返回0. 
 Q: 做一個測試吧,從一個文件中不斷讀取數(shù)據(jù),然后打印出來。 A: 首先,先寫個腳本,向一個文件中寫入100個字符,為依次重復(fù)10次0123456789.     i=0  j=0    while [ $i -lt 100 ];  do      let "j = i % 10"      echo -n $j >> numbers      let "i = i + 1"  done  
執(zhí)行它,會在同目錄下找到一個numbers的文件,使用vi打開確認(rèn)一下內(nèi)容:
 
 現(xiàn)在使用fgets函數(shù)將此文件的數(shù)據(jù)讀出來: #include <stdio.h>  #include <string.h>    #define PRINT_CH(ch)    printf(#ch" is %c\n", (ch));  #define PRINT_STR(str)  printf(#str" is %s\n", (str));    int main (int argc, const char * argv[])  {        FILE *fp = fopen("numbers", "r");      if(fp)      {          char buf[11] = {0};          while(fgets(buf, sizeof(buf), fp) != NULL)          {              if(!ferror(fp))                  PRINT_STR(buf)              else                  PRINT_STR("ferror happens...")                            memset(buf, 0, sizeof(buf));          }          fclose(fp);      }        return 0;  }  
這里,為了分便顯示結(jié)果,緩沖區(qū)被設(shè)置大小為11,最后一個字節(jié)保存結(jié)束符\0, 前10個字符保存0~9.
 運(yùn)行結(jié)果: 
 
 Q: fgets會自動在緩沖區(qū)結(jié)尾加上\0作為結(jié)束符是吧? A: 是的。這和strcpy在這個方面是一致的。不過,fgets也有自己的特點(diǎn),正如gets一樣,遇到回車符,它會提前結(jié)束;如果遇到回車符,而且緩沖區(qū)還沒填滿,那么回車符會被填充到緩沖區(qū)中,最后再加上\0作為結(jié)束符。 看個例子: #include <stdio.h>  #include <string.h>    #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  #define PRINT_STR(str)      printf(#str" is %s\n", (str));    int main (int argc, const char * argv[])  {        char buf[3];      char *ret = fgets(buf, sizeof(buf), stdin);      if(ret)      {          PRINT_D(strlen(buf))          PRINT_STR(buf)      }            return 0;  }  
運(yùn)行:
 輸入h然后回車, 結(jié)果: 
 可以發(fā)現(xiàn)回車字符被保存在了buf中.它和gets在這方面不一致,這是需要注意的。 
 Q: 如果不希望到換行就結(jié)束,也可以指定緩沖區(qū)大小,該怎么辦? A: 可以使用fread(不過它是適用于二進(jìn)制讀的函數(shù),用于文本方式可能在某些情況下出錯,需要小心). size_t  fread(void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream);  
ptr: 表示讀入數(shù)據(jù)保存的地址;
 size: 表示每組數(shù)據(jù)的大?。?/p>
 nitems: 表示讀入數(shù)據(jù)的組數(shù); stream: 表示文件指針; 返回值: 成功讀取數(shù)據(jù)的組數(shù);如果返回NULL,可能是直接遇到文件尾或者讀取出錯,需要用feof和ferror來區(qū)分;當(dāng)然,如果讀取不成功,返回值會比傳入的參數(shù)nitems要小。 這個函數(shù)使用的時候要注意:正因?yàn)樗怯糜诙M(jìn)制讀的,它不會在讀取ok后自動為緩沖區(qū)最后加上\0作為結(jié)束符,這個要小心;如果第二個和第三個參數(shù)互換了,那么返回值也會發(fā)生相應(yīng)改變,這也需要小心。 舉個例子吧: #include <stdio.h>  #include <string.h>    #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  #define PRINT_STR(str)      printf(#str" is %s\n", (str));    int main (int argc, const char * argv[])  {        char buf[3] = {0};      size_t ret = fread(buf, sizeof(buf) - 1, 1, stdin);      if(ret == 1)      {          PRINT_STR(buf)      }            return 0;  }  
可以看到,開始就將buf初始化為0了,fread的第二個參數(shù)是buf總大小減去1的數(shù)值,因?yàn)楹竺姘炎x取的數(shù)據(jù)當(dāng)成char *字符串,最后一個字節(jié)需要保存為\0.
 第三個參數(shù)為1表示1組數(shù)組,第二個參數(shù)表示每組數(shù)據(jù)大小為sizeof(buf) - 1. 最后一個參數(shù)表示從標(biāo)準(zhǔn)輸入stdin讀取。 需要注意:正如之前說過,文件讀寫為交互設(shè)備時,是屬于行緩沖,需要輸入回車來提交緩沖區(qū)。 運(yùn)行: 
 輸入he并回車,可以看到打印了he. 如果輸入h并回車,那么效果如下: 
 可以發(fā)現(xiàn)回車字符被保存在了buf中。 在這里不用太小心,如果輸入多余2個字符,不會發(fā)生緩沖區(qū)溢出,因?yàn)閒read有參數(shù)已經(jīng)標(biāo)志了緩沖區(qū)大小。 
 Q: 上面是關(guān)于文件輸入,文件輸出的fputc, putc, putchar是不是也和fgetc, getc, getchar的關(guān)系類似? A: 是的,使用man putc可以看到它們之間的關(guān)系。 
 
 Q: 標(biāo)準(zhǔn)中也沒有putch和putche么? A: 是的,實(shí)際上在mac系統(tǒng)默認(rèn)下也沒發(fā)現(xiàn)它們的非標(biāo)準(zhǔn)實(shí)現(xiàn)。 
 Q: 字符串輸出的fputs和puts有什么區(qū)別么? A:  
 可以看到,fputs將字符串向指定文件輸出,但是并不會自動加上換行字符;而puts會自動加上換行字符。 另外,fputs返回EOF表示操作失敗,返回非負(fù)數(shù)表示成功;所以判斷fputs是否成功最好使用if(fputs(xxxx) != EOF)或者if(fputs(xxx) == EOF)來表示。 如下代碼: #include <stdio.h>  #include <string.h>    #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  #define PRINT_STR(str)      printf(#str" is %s\n", (str));    int main (int argc, const char * argv[])  {        int ret = fputs("hello", stdout);      if(ret != EOF)      {          PRINT_D(ret)      }            return 0;  }  
執(zhí)行:
 
 可以看到,輸出的hello后面并沒有換行。 puts函數(shù)返回值同fputs. puts的使用: #include <stdio.h>  #include <string.h>    #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  #define PRINT_STR(str)      printf(#str" is %s\n", (str));    int main (int argc, const char * argv[])  {        int ret = puts("hello");      if(ret != EOF)      {          PRINT_D(ret)      }            return 0;  }  
運(yùn)行結(jié)果:
 
 可以看到puts輸出后自動加了換行。 
 Q: 文件輸入和輸出可以像printf和scanf一樣有格式化操作么? A: 當(dāng)然可以了,printf和scanf只是文件輸入輸出的一個特例而已。 fprintf函數(shù)和fscanf函數(shù)可以實(shí)現(xiàn)此功能。 int  fprintf(FILE * restrict stream, const char * restrict format, ...);  
int  fscanf(FILE *restrict stream, const char *restrict format, ...);  
可以看到,它們和printf和scanf很相似,除了第一個參數(shù)表示文件指針,對于printf也就是fprintf(stdout, xxx)的特例;scanf也就是fscanf(stdin, xxx)的特例。
 
 不過,fprintf還可以用文件指針stderr表示錯誤輸出,它是不帶緩沖的輸出。 fprintf的返回值表示成功輸出的字符字節(jié)個數(shù),printf的返回值和它一致。 fscanf返回值表示成功輸入的變量個數(shù)。 這里不做更多說明了。 
 Q: 一直提到的文件句柄,它到底是什么? A:  typedef struct __sFILE {      unsigned char *_p;        int _r;           int _w;           short   _flags;           short   _file;            struct  __sbuf _bf;       int _lbfsize;                 void    *_cookie;         int (*_close)(void *);      int (*_read) (void *, char *, int);      fpos_t  (*_seek) (void *, fpos_t, int);      int (*_write)(void *, const char *, int);              struct  __sbuf _ub;       struct __sFILEX *_extra;       int _ur;                      unsigned char _ubuf[3];       unsigned char _nbuf[1];               struct  __sbuf _lb;               int _blksize;         fpos_t  _offset;      } FILE;  
可以看到它封裝了操作文件的緩沖區(qū)、當(dāng)前位置等信息,這也能很好地體現(xiàn)帶緩沖的事實(shí)。比如,fopen函數(shù),它和open函數(shù)的一大區(qū)別就是是否使用用戶層緩沖。
 
 Q: fopen和open的關(guān)系是什么樣子的? A: open是POSIX標(biāo)準(zhǔn),也是基于Unix系統(tǒng)的系統(tǒng)調(diào)用。fopen是C語言標(biāo)準(zhǔn),它的內(nèi)部當(dāng)然必須調(diào)用open系統(tǒng)調(diào)用才能完成真正功能。 
 Q: 給個使用open, read, write, close函數(shù)的例子吧。 A:  首先使用echo命令創(chuàng)建一個內(nèi)容為hello的文件,文件名為test. 然后編寫如下代碼:
 #include <stdio.h>  #include <string.h>  #include <fcntl.h>  #include <unistd.h>    #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  #define PRINT_STR(str)      printf(#str" is %s\n", (str));    int main (int argc, const char * argv[])  {      char buf[32] = {0};      ssize_t ret;      int file = open("test", O_RDONLY);        if(file < 0)      {          perror("open file error");          return -1;      }      ret = read(file, buf, sizeof(buf));      if(ret > 0)          PRINT_STR(buf)                close(file);            return 0;  }  
運(yùn)行,結(jié)果為:
 
 
 Q: 我想在文件開頭插入一個字符,用如下的代碼怎么不行? #include <stdio.h>  #include <string.h>  #include <fcntl.h>  #include <unistd.h>    #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  #define PRINT_STR(str)      printf(#str" is %s\n", (str));    int main (int argc, const char * argv[])  {      FILE *fp = fopen("test", "r+");      if(!fp)      {          perror("fopen error");          return -1;      }      fseek(fp, 0, SEEK_SET);      if(fputc('a', fp) == EOF)          perror("fputc error");            fclose(fp);            return 0;  }  
本來test文件里面的內(nèi)容是hello\n,執(zhí)行上面的程序后,為什么不是ahello\n,而是aello\n ?
 A: 這個問題的原因在于fputc, fputs, fwrite等寫操作的函數(shù)均為覆蓋寫,不是插入寫導(dǎo)致的。如果需要將文件讀出來,將文件開頭插入字符a,然后將讀出的數(shù)據(jù)全部追加到文件后面,這樣才行。 
 Q: 一直聽到二進(jìn)制文件和文本文件,它們到底有什么區(qū)別? A: 計算機(jī)底層最終只能處理所謂的二進(jìn)制文件,文本文件只是人們將文件的內(nèi)容做了抽象得到的一種特殊的二進(jìn)制文件。不過,它們是有一定區(qū)別的,可能在某些時候,不同的平臺,它們的表現(xiàn)也不同。但是從理論上來說,二進(jìn)制文件可能更節(jié)省空間,這一方面是因?yàn)槎M(jìn)制文件是文件最終的形式,另一方面是因?yàn)榭梢杂枚M(jìn)制的1比特表示數(shù)值1,甚至整形1,但是用文本方式,卻至少用1字節(jié)。另外,不同平臺對于回車換行,CR與LF, "\r\n"的表示方式不太一致,導(dǎo)致對于它的文本解讀和二進(jìn)制形式有不一致的地方。在mac上,文本形式的換行用'\n'表示,而在windows上,文本換行是\r\n, 所以有時會發(fā)現(xiàn)同一個文件在不同操作系統(tǒng)下的換行顯示會有不同。不過,二進(jìn)制和文本方式讀取也不是水火不相容的,正如下面的例子,顯示的結(jié)論沒什么不同: 假設(shè)test文件中保存hello\n, 文本方式讀: #include <stdio.h>  #include <string.h>  #include <fcntl.h>  #include <unistd.h>    #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  #define PRINT_STR(str)      printf(#str" is %s\n", (str));    int main (int argc, const char * argv[])  {      int ch;      FILE *fp = fopen("test", "rt");      if(!fp)      {          perror("fopen error");          return -1;      }      while ((ch = fgetc(fp)) != EOF)      {          printf("ch:%c %d\n", ch == '\n' ? 'N' : ch, ch);      }            fclose(fp);            return 0;  }  
二進(jìn)制讀:
 #include <stdio.h>  #include <string.h>  #include <fcntl.h>  #include <unistd.h>    #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  #define PRINT_STR(str)      printf(#str" is %s\n", (str));    int main (int argc, const char * argv[])  {      int ch;      FILE *fp = fopen("test", "rb");      if(!fp)      {          perror("fopen error");          return -1;      }      while ((ch = fgetc(fp)) != EOF)      {          printf("ch:%c %d\n", ch == '\n' ? 'N' : ch, ch);      }            fclose(fp);            return 0;  }  
二個應(yīng)用程序的運(yùn)行結(jié)果均為:
 
 顯示為N的地方為換行字符。 
 Q: 有的時候,想要直接將一個整形數(shù)據(jù)以二進(jìn)制形式寫入文件中,有什么更分便的調(diào)用方式? A: putw可以實(shí)現(xiàn)你要的功能。 #include <stdio.h>  #include <string.h>  #include <fcntl.h>  #include <unistd.h>    #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  #define PRINT_STR(str)      printf(#str" is %s\n", (str));    int main (int argc, const char * argv[])  {      FILE *fp = fopen("test", "w");      if(!fp)      {          perror("fopen error");          return -1;      }      putw(32767, fp);        fclose(fp);            return 0;  }  
運(yùn)行完后,用hexdump test查看test文件的十六進(jìn)制形式,可以發(fā)現(xiàn)32767對應(yīng)int類型大小的數(shù)據(jù)已經(jīng)保存在test中了。
 當(dāng)然,如果不嫌麻煩,可以使用如下的代碼實(shí)現(xiàn)上面類似的功能: #include <stdio.h>  #include <string.h>  #include <fcntl.h>  #include <unistd.h>    #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  #define PRINT_STR(str)      printf(#str" is %s\n", (str));    int main (int argc, const char * argv[])  {      int n = 32767;      char *pn = (char *)&n;      int i;      FILE *fp = fopen("test", "w");      if(!fp)      {          perror("fopen error");          return -1;      }      for(i = 0; i < sizeof(n); ++i)      {          fputc(*(pn + i), fp);      }        fclose(fp);            return 0;  }  
 Q: 之前看過好多關(guān)于文件打開方式的字符串,"rb", "w+"等等,到底怎么很好地明白它們的打開方式? A: 其實(shí)很簡單。以r開頭的方式打開,如果文件不存在必然失?。淮蜷_方式含有b即表示是二進(jìn)制方式,如果沒有b或者有t,那就說明是文本方式;如果打開方式中有+,那么表示可讀可寫; 
 Q: 很多時候,如果讀取輸入緩沖區(qū)的數(shù)據(jù)讀多了,想放回去怎么辦? A: 可以使用ungetc函數(shù)。 舉個例子吧:
 從標(biāo)準(zhǔn)輸入讀取一個十進(jìn)制整數(shù)和最后一個分隔符(不是十進(jìn)制整數(shù)字符),然后打印這個整數(shù)和最后的分隔符。 #include <stdio.h>  #include <string.h>  #include <fcntl.h>  #include <unistd.h>  #include <ctype.h>    #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  #define PRINT_STR(str)      printf(#str" is %s\n", (str));    int main (int argc, const char * argv[])  {      int n = 0;      int ch;      char end_ch;            while ((ch = getchar()) != EOF && isdigit(ch))      {          n = 10 *n + (ch - '0');      }      if(ch != EOF)      {          ungetc(ch, stdin);      }      end_ch = getchar();            PRINT_D(n)      PRINT_CH(end_ch)            return 0;  }  
運(yùn)行時,輸入1234;然后換行,
 
 可以發(fā)現(xiàn),ungetc會將;字符重新放回輸入流中,是的end_ch被賦值為;字符; 如果沒有使用ungetc的話: 
 可以發(fā)現(xiàn),end_ch被賦值為輸入1234;后面的換行了。 
 Q: 關(guān)于輸入輸出重定向已經(jīng)聽說了很多了,c語言中有函數(shù)可以實(shí)現(xiàn)重定向嗎? A: 是的, freopen函數(shù)可以實(shí)現(xiàn)這個作用。 FILE *freopen(const char *restrict filename, const char *restrict mode, FILE *restrict stream);  
第一個參數(shù)表示需要重定向的文件位置;第二個參數(shù)表示重定向方式,如"w"為寫方式;第三個參數(shù)表示被重定向的文件句柄;
 下面有個代碼將展示如何將stdout重定向到test文件,然后再恢復(fù)stdout的輸出; 代碼如下: #include <stdio.h>  #include <string.h>  #include <fcntl.h>  #include <unistd.h>  #include <ctype.h>    #define PRINT_D(intValue)   printf(#intValue" is %lu\n", (intValue));  #define PRINT_CH(ch)        printf(#ch" is %c\n", (ch));  #define PRINT_STR(str)      printf(#str" is %s\n", (str));    int main (int argc, const char * argv[])  {      FILE *file;      int fd;      fpos_t  pos;                  fprintf(stdout, "1111");                  fflush(stdout);      fgetpos(stdout, &pos);      fd = dup(fileno(stdout));              file = freopen("test", "w", stdout);      if(!file)      {          perror("freopen error");          return -1;      }                  fprintf(stdout, "hello");                  fflush(stdout);      dup2(fd, fileno(stdout));      close(fd);      clearerr(stdout);      fsetpos(stdout, &pos);                  fprintf(stdout, "2222");            return 0;  }  
 運(yùn)行結(jié)果: 
 可以看出,最開始的標(biāo)準(zhǔn)輸出和最后的標(biāo)準(zhǔn)輸出都正常顯示在屏幕,第二個標(biāo)準(zhǔn)輸出因?yàn)楸恢囟ㄏ蜉敵龅搅藅est文件中;查看test文件的內(nèi)容: 
 不過,如果是在windows平臺,恢復(fù)stdout的方式和上面的代碼可能不一致。 
 xichen 2012-5-12 18:08:31 |