測試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
*/
