今天继续看牛人做过的东西,这个小程序并不大,加上相当多的注释行,才5000多行。这个小程序是在linux下实现的,之前自己也一直用vi来看并加以更加详细的注释,但是效率实在太低。于是将其转移到windows下决定改造到VS2012下运行。

这是一段纯C的代码,新建的工程是C++的,而代码中使用了强制类型转换将一个结构体类型转换成了另一个结构体。于是编译的时候报错不能通过。最后,我新建了空工程,将其以已存在的文件的形式导入,解决了这个问题。修改了一些问题之后,终于不报错,可以运行了。可是真正悲催的事情开始发生了。

首先,运行之后报错,确定了是文件读写的错误之后,给原来的代码中打开文件的地方加上异常处理。可是还是错误。单步进去发现

while(fread(&record,sizeof(RECORD_TYPE),,fp_data)==)

怎么都不能进入循环,fread的返回值永远都是0。因为sizeof(RECORD_TYPE)的值是128,于是把上面的代码改成了

while(fread(&record,,,fp_data)==)

再来单步一看,好奇怪,这次返回的值变成了33。百思不得其解。之后我把这句代码拿出来,不放在while循环中,如下:

fseek(fp_data,0L,SEEK_END);//偏移都文件尾部
pos=ftell(fp_data);//读取尾部所在位置
fseek(fp_data,0L,SEEK_SET);//偏移到文件头部
err = fread(&recode,,,fp_data);//读取一条记录
pos = ftell(fp_data);//读取位置

之后再调试,发现前一个ftell得到的值是81792,而后一个ftell的返回值却是4096。更让人费解了。文件并没有到达文件末尾,文件足够大,才读取了4096/81792,可是,明明只读取了128位字节,128*8应该是1024才对,怎么第2个ftell返回了4096?这种情况下,有些慌了。虽然看了MSDN上的关于fread放回值得说明,可是却没有去实践。最后度娘告诉我这个网址http://www.360doc.com/content/11/0128/16/2150347_89591799.shtml

/*
文本方式读取二进制数据, 可能在文件结束之前将某段数据判定为文件末尾EOF, 所以结束读取( 举个例子, 比如遇到 0x00 0x00 0xff 0xff, 则文本方式方式的文件流, 认为已经到文件末尾, 不能读取)
*/

瞬间明白了错误的原因。对上一段代码修改一下再试:

fseek(fp_data,0L,SEEK_END);//偏移都文件尾部
pos=ftell(fp_data);//读取尾部所在位置
fseek(fp_data,0L,SEEK_SET);//偏移到文件头部
err = fread(&recode,,,fp_data);//读取一条记录
pos = ftell(fp_data);//读取位置
err = feof(fp_data);//是否到达文件尾,非0为经过了文件尾,0为否;feof具体用法见msdn

得到了feof返回值16,非0,表达经过了文件尾。因而断定fread读取数据的时候遇到了误以为的EOF标志。修正方法为,将fopen中的mode参数改成了'rb’,即由文本方式读取改为二进制流的方式读取。如下:

//if (err=(fopen_s(&fp_data,"data","r")) != 0)
// printf("open file data failed\n"); //打开数据文件 if (err=(fopen_s(&fp_data,"data","rb")) != )
printf("open file data failed\n"); //打开数据文件

这个问题总算解决了。满以为就此解决了问题。偏偏陷入了令人更加头疼的境地。

再次点击运行,又是运行时错误。跟进去发现了出现了一个树的指针为空,却赋值了。再看明明调用了给这个节点申请了空间啊!难道是malloc失败?立马给malloc的地方加上判断。遗憾的是,依然一点进展都没有。再看作者的写代码的思路,给这个节点申请空间之前,先判断a的值是不是等于b,如果不等于,则打印一条消息新建树节点失败,但不终止程序,当然也不申请空间。噢,原来这样,那看看什么时候会出现这种情况。找到这段代码的上面一段代码,密密麻麻的一片,几个if-else写了一两百行。不过还是可以大致明白的。

头脑一震,发现问题了,作者代码大致如下:

if (a < b)
{
//.......一大段代码,好几十行
}
else
{
if (a != b)
{
print("");
return FALSE;
}
//给节点申请空间
//......一大段代码
}

所以我满心欢喜的认为即使作者这样的牛人也会犯迷糊,觉得理应将此处的a != b改成 a >= b。不管它能不能改,先改了再说。可是改了之后,运行一下,这次还是报错了,不过呢,不是这个地方了。单步调试,这个a的值怎么打大得太奇怪了吧。先看代码:

//此处a,b,c,d等都不是程序中的原样,只是为了说明而做了简化
found=FALSE;
if (a < b){
i = ;
while (!found && i<a){
if (c >=d[i]){//其中b就是数组d的大小
i++;
} else {
found = TRUE;
}
}
//其他代码
}

再一看b的值是29,可是a的值却是5029。不发生越界才奇怪了。从此处也就明白了上一段代码中为何只判断a < b 及 a != b 而不处理 a > b 了,因为一旦 a > b 就出错了。

回头一想怎么会这样呢?

到底是哪儿错了???

想到了一点,会不会还是读取文件的时候的错误?会不会是因为以文本文件流方式读取了二进制文件,影响了a的取值?果断找到所有用到fopen的地方,把mode参数加上一个 'b' 。

再次点击运行,果然,问题就这么解决了。

总结

这么一场闹剧总算可以收场了,都是因为fread这个函数惹得祸。

/*
文本方式不能完全读取, 而二进制方式能的原因-
文本方式读取文件, 最主要的用处是一次读取一整句( 以换行符'\n', 即二进制的换行标志"\r\n"结束 ), 方便用于特殊用处ReadString、fscanf(...,"%s",...)之类, 每次读取的内容长度是不定的; 而二进制读取方式Read、fread等, 都是读取固定长度
所以文本方式读取对EOF的判定, 是一个文件尾结束标志, 如果是文本文件, 则这个文件尾肯定不会出现在文件内容中( 因为是不可打印字符构成的结束标志, 人可读的文本文件不会包括它 ), 这样以结束标志为文件尾则是可以的; 二进制文件内容可以是任意字节, 如果把它当文本文件来读, 以文件尾为结束, 当然可能出现把文件内容判定为文件尾的情况;
二进制读取方式由于每次读取固定字节, 所以只需要用总文件长度( 这个数值是系统管理的数值, 不是计算得出来的 )减去每次读取的长度( 或根据Seek的位置计算长度 ), 就可以知道是否到文件尾, 不需要定义结束标志; 所以用二进制方式打开任何文件都是合理的
*/

至此也明白了作者是通过读取到的数据量是否等于fread 中count参数要求的数据量来结束循环而不是通过feof来判断。

最后,一定要明白,使用fread/fwrite的时候千万记得以二进制形式读取。

那些年,坑死自己的事之fread/fwrite的更多相关文章

  1. 【Python3爬虫】百度一下,坑死你?

    一.写在前面 这个标题是借用的路人甲大佬的一篇文章的标题(百度一下,坑死你),而且这次的爬虫也是看了这篇文章后才写出来的,感兴趣的可以先看下这篇文章. 前段时间有篇文章<搜索引擎百度已死> ...

  2. 记一次FTP下载踩坑的故(shi)事(gu)

    下班前领导忽然要求我将客户的日志服务器上一些日志拷贝到测试服务器中,不过领导只提供给我FTP的连接方式,很明显就是要我用FTP方式去做啦 一般来说FTP批量下载也就上网随便找个脚本的事,但是却成了我疯 ...

  3. 一些Layout的坑。坑死我自己了

    iOS这个东西,初学感觉,还好还好,然后一年之后再来修复一下初学的时候的代码,我只是感觉头很晕- - 别扶我. AutoLayout的坑,明明以前都没有的!!!升了iOS10就突然发现了这个坑,其实也 ...

  4. JQuery中动态生成元素的绑定事件(坑死宝宝了)

    今天在做项目的时候,遇到了一个前端的问题,坑了我好长时间没有解决,今天就记录于此,也分享给大家. 问题是这样的,首先看看我的界面,有一个初始印象: 下面是操作列所对应的JS代码: { "da ...

  5. ZOJ问题(坑死了)

    ZOJ问题 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  6. 入坑java工程师那些事

    最近在知乎上看到好多关于转行做java的朋友,有的在担心学历,有的在想着如何学习java,有的在纠结如何面试.作为一个工作了近10年的java程序员来聊聊我的一些想法,主要从学历.个人能力.工作环境等 ...

  7. 坑死我啊,一个WPF Adorner使用注意事项

    1.见鬼了? 项目中遇到这样的要求,一个Button用一个Adorner装饰,这个Adorner上又有一个Button,如下面这样 此时,我们在点击小Button的时候只希望处理小Button的事件, ...

  8. 金山助手流氓软件-被进程sjk_daemon.exe坑死

    修改完Android工程代码,进入调试阶段时DDMS中报错:The connection to adb is down, and a severe error has occured. 由于之前也碰到 ...

  9. hdu 2822 ~!!!!!!坑死我

    首先 在此哀悼...  为我逝去的时间哀悼...  每一步都确定再去写下一步吧...日狗 不过还是有点收获的..  对优先队列的使用 有了进一步的理解 先上代码 #include<iostrea ...

随机推荐

  1. 根据判断PC浏览器类型和手机屏幕像素自动调用不同CSS的代码

    1.媒体查询方法在 css 里面这样写 -------------------- @media screen and (min-width: 320px) and (max-width: 480px) ...

  2. postman 测试API - token

    1.使用全局变量保存token 2.再调用 参考文章 http://www.jianshu.com/p/13c8017bb5c8 https://testerhome.com/topics/6555

  3. Swift中的类型转换

    写在前面:1,类型转换的两种方式 2,as!和as?的用法 3,类型判断中 is和===的用法 类型转换方式一,利用类型的构造器进行转换 let str = " var i = Int(st ...

  4. JS循环语句作业讲解(折纸、兔子生兔子、买东西组合)

    1.一张纸的厚度是0.0001米,将纸对折,对折多少次厚度超过珠峰高度8848米: varn = 0;varg = 0.0001;while(){ g= g *2; n++ (g>8848bre ...

  5. PBR

    https://iwantthatcake.wordpress.com/2012/02/22/real-time-radiosity-geometrics-enlighten/ http://www. ...

  6. 静态方法中不能new内部类的实例对象的总结

    class Test{ public void main(String[] args){ A testA=new A(); //这里会出现问题 new Thread(new Runnable(){ p ...

  7. Android studio 软件板块

  8. git pull和git fetch的区别

    Git中从远程的分支获取最新的版本到本地有这样2个命令:1. git fetch:相当于是从远程获取最新版本到本地,不会自动merge Git fetch origin master git log ...

  9. 第3月30天 UIImage imageWithContentsOfFile卡顿 Can't add self as subview MPMoviePlayerControlle rcrash

    1. UIImage imageWithContentsOfFile卡顿 [[UIImage alloc] initWithContentsOfFile 卡顿 2.uitableview scroll ...

  10. VS2010+Qt5.4.0 环境搭建(离线安装)

    原创作者:http://blog.csdn.net/solomon1558/article/details/44084969 前言 因项目需要Qt开发GUI,我根据网上资料及自己的经验整理了搭建vs2 ...