今天继续看牛人做过的东西,这个小程序并不大,加上相当多的注释行,才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. 子元素使用float 父元素撑开方法

    一个Div包含了多个子Div,并且子Div使用了浮动后,父Div确不能被撑开,如下图: 部分代码如下: 1 <style> 2   #div1{border:1px solid red;f ...

  2. canvas-渐变文字

    html要求: <body onload="draw('canvas');"> body这里的onload一定要写,在这个处理模式下,是在body这里执行加载页面完成后 ...

  3. 12月18日Smarty文件缓存

    缓存 做缓存的目的是为了让程序运行起来更加迅速.因为如果程序访问数据库时数据量较大,执行起来会比较慢.而且每一次刷新页面都会访问依稀数据库,然后再把数据显示在页面上. 设置缓存也有一个缺点,那就是缓存 ...

  4. oracle一次给多表添加相同字段

    遇到一个需求:在已经建好的数据库中,为每一个数据表都添加相同的3个字段. 分析:数据库中的数据表较多,一一手动修改耗时低效,是否可以用程序遍历每一张表,然后为遍历到的当前表添加字段? 查询当前用户的所 ...

  5. mysql-开启慢查询&所有操作记录日志

    在运营网站的过程中,可能会遇到网站突然变慢的问题,一般情况下和 MySQL 慢有关系,可以通过开启慢查询,找到影响效率的 SQL ,然后采取相应的措施.下面介绍一下如何开启慢查询: 1.开启慢查询 找 ...

  6. 保留password模式文本框textbox内的数据不丢失。

    在asp.net 2.0环境下,使用textbox,提交到服务器再传回,如果textbox是password模式的,那么textbox内的密码(星号),就没有了! protected override ...

  7. 基于jQuery的Validate表单验证

    表单验证可以说在前端开发工作中是无处不在的~ 有数据,有登录,有表单, 都需要前端验证~~  而我工作中用到最多的就是基于基于jQuery的Validate表单验证~  就向下面这样~ 因为今天有个朋 ...

  8. xml中处理大于小与符号

    原符号   <    <=    >    >=     &      '       " 替换符号 < <= > >= & ...

  9. bootstrap Table 中给某一特定值设置table选中

    bootstrap Table 中给某一特定值设置table选中 需求: 如图所示:左边地图人员选定,右边表格相应选中. 功能代码: //表格和图标联动 function changeTableSel ...

  10. 现代软件工程作业 第二章 学习github笔记

    在网上大量资料的辅助下,学习了github的基本使用方法,尝试了一些常见的命令.为了便于记忆总结了自己的学习内容. 1.首先需要在github的官网上注册一个帐号,并新建一个repository,选这 ...