GDB调试字符数组时指针和数组区别的体现
测试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 */