我曾经在http://blog.csdn.net/stpeace/article/details/42847697中说过:fwrite和fread函数针对的是字符(无边界), 而不是字符串(以'\0'作为边界)
今天呢, 某模块出了一个问题。 据说, 这个问题搞了较长时间。我准备晚上去打球的,毕竟感觉好久没有打了。 结果一个电话过来, 说有个紧急问题需要我协助定位, 我就乖乖返回了。经了解问题现象并查看源代码, 我立即发现了fread函数用法错误。后来修改并验证, 果然如此。
fread函数错误用法的程序抽象为:
#include <iostream>
using namespace std;
typedef struct _node
{
char name[100];
int score;
}Student;
int main()
{
Student s;
memset(&s, 0, sizeof(s));
strncpy(s.name, "Eric", sizeof(s.name) - 1);
s.score = 99;
FILE *fp = fopen("taoge", "wb");
fwrite(&s, sizeof(s), 1, fp);
fwrite(&s, sizeof(s), 1, fp);
fwrite(&s, sizeof(s), 1, fp);
fclose(fp);
Student t;
memset(&t, 0, sizeof(t));
fp = fopen("taoge", "rb");
fread(&t, sizeof(t) - 1, 1, fp); // danger, logical error
cout << t.name << endl;
cout << t.score << endl;
memset(&t, 0, sizeof(t));
fread(&t, sizeof(t) - 1, 1, fp); // danger, logical error
cout << t.name << endl;
cout << t.score << endl;
memset(&t, 0, sizeof(t));
fread(&t, sizeof(t) - 1, 1, fp); // danger, logical error
cout << t.name << endl;
cout << t.score << endl;
fclose(fp);
return 0;
}
结果为:
Eric
99
25344
6488064
我们看到, fread少读了一个字符, 结果会导致后面读取的时候出现整体偏移错误。 应该修改为:
#include <iostream>
using namespace std;
typedef struct _node
{
char name[100];
int score;
}Student;
int main()
{
Student s;
memset(&s, 0, sizeof(s));
strncpy(s.name, "Eric", sizeof(s.name) - 1);
s.score = 99;
FILE *fp = fopen("taoge", "wb");
fwrite(&s, sizeof(s), 1, fp);
fwrite(&s, sizeof(s), 1, fp);
fwrite(&s, sizeof(s), 1, fp);
fclose(fp);
Student t;
memset(&t, 0, sizeof(t));
fp = fopen("taoge", "rb");
fread(&t, sizeof(t), 1, fp); // ok
cout << t.name << endl;
cout << t.score << endl;
memset(&t, 0, sizeof(t));
fread(&t, sizeof(t), 1, fp); // ok
cout << t.name << endl;
cout << t.score << endl;
memset(&t, 0, sizeof(t));
fread(&t, sizeof(t), 1, fp); // ok
cout << t.name << endl;
cout << t.score << endl;
fclose(fp);
return 0;
}
结果:
Eric
99
Eric
99
Eric
99
正常。
为什么写代码的人当时要用sizeof(...) - 1呢?
根本原因: 没有搞清楚fread函数的用法。在这里, 我再次强调一遍: fread, read, recv, fwrite, write, send这类函数针对的是字符(无边界), 而不是字符串(以'\0'作为边界), 与'\0'没有半毛钱的关系。
间接原因: 我们经常强调数组szTest必须以'\0'结尾, 可能是当时写该代码的人受了这个思想的影响, 以为任何缓冲区的最后一定是'\0'.
我以前总结过类似问题, 所以今天很快帮助其他同事解决了此问题, 好开心, 尽管最后球没打成。
在其位, 谋其事, 体现自身价值。
台上1分钟, 台下1天功啊, 以后继续坚持学习、实践、总结、分享。 好吧, 本文就总结到这里。