測試ftell函數時發現報錯,先貼源碼
// File Name: ftell.c #include <stdio.h> #include <stdlib.h> int main(int argc, char** argv) { FILE* fp = fopen("myfile.in", "r"); if (fp == NULL) { perror("fopen error"); exit(1); } char buf[4]; fgets(buf, 4, fp); if (fputs(buf, fp) == EOF) { perror("fputs error"); exit(1); } if (ferror(fp)) { perror("ferror"); exit(1); } return 0; }
錯誤信息如下
於是用GDB調試,在fputs處設斷點,輸出字符數組
突然我想查看每個字符的值,於是看到的是這個
啊,突然想起來,buf的類型並不是char*,雖然如果作為函數輸入參數的話會被當成char*,但是buf的實際類型是char (*)[4]
所以輸出的是4個char (*)[4],也就是buf開始的16個字符
但是我使用p &buf[0]@sizeof(buf)會報錯Only values in memory can be extended with '@'
想着可能需要類型轉換,加了(char*)后還是報錯,原來是因為只有值才能狗用@擴展,GDB會取得值的指針,然后用@往前移動
於是幾個調試如下
(gdb) p buf $1 = "lin" (gdb) p &buf[0] $2 = 0x7fffffffde90 "lin" (gdb) p &buf[0]@4 Only values in memory can be extended with '@'. (gdb) p (char*)&buf[0]@4 Only values in memory can be extended with '@'. (gdb) p *(char*)&buf[0]@4 $3 = "lin" (gdb) p *buf@4 $4 = "lin"
\0是看不到的,除非單獨查看那一位的字符
(gdb) p (int)buf[3] $5 = 0 (gdb) p buf[3] $6 = 0 '\000'
OK,繼續解決fputs出錯的問題吧。
其實perror顯示的信息很完美了,錯誤原因是Bad file descriptor,在這里文件描述符藏在FILE*指向的對象里,錯誤也就是fp。
這里我是要把信息輸出到屏幕上,所以fputs的第二個輸入參數應該是標准輸出stdout,而不是我打開的文件指針fp。
由於fopen選擇了讀取模式,所以無法進行寫入。
試着把fopen第二個參數改成"r+",允許寫入,結果如下
該文本之前第1行是line 01,現在被改成了linlin1,因為讀取3個字符時偏移量是3(即'e'所在位置),然后又寫入了3個字符,所以"lin"替代的是"e 0"。
這里也可以發現,用"r"而不是"r+"來禁止寫入能夠檢查出一些容易忽視的錯誤。
修改后如下
// File Name: ftell.c #include <stdio.h> #include <stdlib.h> int main(int argc, char** argv) { FILE* fp = fopen("myfile.in", "r"); if (fp == NULL) { perror("fopen error"); exit(1); } char buf[4]; fgets(buf, 4, fp); if (fputs(buf, stdout) == EOF) { perror("fputs error"); exit(1); } printf("\nfile offset: %ld\n", ftell(fp)); if (ferror(fp)) { perror("ferror"); exit(1); } fclose(fp); return 0; } /* output: lin file offset: 3 */