我曾经在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天功啊, 以后继续坚持学习、实践、总结、分享。 好吧, 本文就总结到这里。