原创 作者:羊仨
hello! 我是羊先生,本文是自己记录的一个小故事,也算是给自己一个教训,同时也分享了解决问题的一点经验吧!
作为新手程序员,以为消完warning就万事大吉了!开心地向领导汇报说今天调完格式就能提交了!
然而!!!!!!!!!!!!!!出现了广大程序员最常见的问题!!!!!!!!!!!!!
!!!段错误(核心已转储)!!!!
遇到这种问题,虽然很着急,但是也大致了解是什么情况!
遇到这种情况,不必着急:
- 大概率是指针偏移,出现野指针;
- 内存泄漏,没有及时free;
解决方案:
- 主程序打printf(“task mark”),对printf没有使用限制的项目中可以采用;
- 简单程序也可以使用gcc单步调试,网上搜即可;
- 对于复杂的项目工程,目前知道的也就只能查内存和变量了;
解决过程: 【以上给出解决思路,下面是我的解决过程,着急的话可以不必看】
2019年9月27日 周五
由于程序不是很长,带上注释也就1000多行,我就直接采用printf的方式定位错误;
在编写程序时,也基本按照编码规范来的,所以在指针指空的时间,会出现打印信息,根据信息提示很快找到出现BUG的大致位置;
最开始没有仔细分析,百度的答案基本都是没有free,没有初始化。
根据这个猜想,我定位BUG原因:
队列中的节点出队后未释放内存?或者某处临时节点使用后没有free?
所以我花了2个小时重新梳理一遍代码,没有发先内存溢出的情况,此时感觉BUG原因定位有误,但是又不敢相信自己。
此时我想到的是再缩小BUG产生的范围,在函数体内部打log,根据BUG位置,在树的遍历函数中打log;
此时已经发现该程序执行了,但是没有执行结束,问题就出现在这里,而这个函数中调用多个函数,包括节点初始化、队列初始化、进队、出队、目录节点深入遍历等;
如果是内存溢出问题,可能就是在队列销毁时出现问题,但是程序未执行到此处。四个小时过去,我决定相信自己,一定是指针飘了。
野指针问题!
针对这个原因,我继续打log,在节点遍历的函数中打印出节点所在的目录,如下程序所示,这样我就可以看到目录遍历的过程了。
dirp = opendir(node_dir->file_path);
if (dirp == NULL) {
printf("dir %s is not found!\n", node_dir->file_path);
/*
* return;
*/
} else {
printf("dir %s is OK!\n", node_dir->file_path);
}
while (1) {
dp = readdir(dirp);
printf("dp = %p ----- NULL = %p\n", dp, NULL);
if (dp == NULL) {
printf("readdir not found!\n");
break;
}
printf("task mark @******@: 546\n");
打印出如下图示所示的log,发现目录遍历没有任何问题,完整的遍历了,而且所有文件都读取到了!而且目录文件读取完最后一个文件后指向NULL安全跳出!!!这这这!!!!不是在遍历目录节点时的问题!
此时已经晚上22.30左右了,平复下心情,关闭程序,在A4纸上梳理数据结构和程序执行过程;
目录节点是作为树的叶子存在的,遍历完目录节点并不是树遍历完成了,所以还会进入下一个节点,是目录节点遍历的问题还是树遍历的问题?
我把树画出来,突然发现树必然是遍历完了,因为既然是一个嵌套的目录能够遍历,其它的节点也能够遍历,而程序未跳出!!!
树的最后一个节点后,未指向NULL!!!!
一定是这个问题,虽然我看不到,但是一定是这样的;
当我找到问题根源后,安心下班回家!
2019年9月18日 周六
第二天周六,很兴奋,一早就起来去公司,去验证我的猜想!果然是这样的,(其实有点经验的,看看程序基本一眼都能够定位到这里的,我还是太年轻);
虽然BUG的根源找到了,但是产生这种BUG也会有很多方式,网上给出意见是:节点未初始化!!!
我再去看程序,不是这样的问题!
那原因只有一个了!! 程序在执行的过程中改变了树的最后一个节点的next !!能够做到这个事情的只有一个函数就是目录节点遍历函数!!!!
此时已经找到BUG原因、BUG位置;而我发现我在遍历树的过程中,调用了两次目录节点遍历函数,第一次是为在初始化队列,第二次是为了查看节点属性。
注释掉第一次调用,程序运行正常!问题就在第一次调用时,导致树的最后一个节点的next值非NULL了。
看到这里以为结束!! 我也以为结束了才来写的博客!!!! 但是不是的!!!!
/* 找到头节点,从头节点开始 */
node = create_node(dst_rootpath);
enlinkqueue(linkqueue, node);
/*
* 成功定位问题所在,BUG所在函数,执行下面函数后改变了树的形状
traversal_dir(node, linkqueue);
*/
/*
*
*打节点测试
*
*/
printf("task mark traversal_: @@@@@@@@@;;; line:1019 \n\n");
/* 找到队列头,节点下移 */
first_node = get_frontqueue(linkqueue);
first_node = first_node->next;
/* 遍历目标目录 */
while (first_node) {
/* 在对比树中查找相同节点 */
node_tree = get_node_tree(tree_rootnode, first_node, dst_rootpath);
/*
*
*打节点测试
*
*/
printf("task mark traversal_: @@@@@@@@@;;; line:1032 \n\n");
11:00---------------18:00
我不想再写了,我要认真起来了!!!!在这期间不断打log,重申代码,...
找到了问题的根源!!!!!! 这次一定是真的了!!!!!
904执行完,905没有执行,直接产生段错误!!!free时出现错误!!
根源:无奈采用gdp调试,程序需要解决两个目录的对比,在3A文件夹下有一个文件,需要读取,首先开辟缓冲区的空间,开辟结束后是正常的。接着进行源目录的读取,源目录读取后是正常的,但是目的目录读取后发现:
第二个read后buf_src和buf_dst的地址是相同的!!!
产生这种现象的原因是:read的两个文件名字、内容相同。实测当两个文件内容不相同时不会出错!!!!
-----20:40------- 产生这种现象的原因是什么?
-----------------------晚上和朋友喝了点酒-------------------- 回去也没睡好觉---------------
2019年9月29日 周日
一早来公司,找同事讨论,我给出了问题的出处和一些原因,询问会不会在别的程序中出现错误,他们给我的答案都是:问题就出现在这个函数内部,而且就在问题点附近。我也有相同的感觉,因为程序别的地方对这里的影响几乎没有。
短短40行程序,问题就出现在这40行程序中!!!!
根据GDP调试的BUG点,我猜想是open函数中的参数是一样的,导致读出来的fd标志是一样的,所以read到的文件是一样的,因此会产生double free段错误。
此时已经将问题定位在下面9行程序中:
输出内容:
这问题已经很明显了!!!
??????我copy了指针?????????
我竟然在对比两个文件时使用了memcpy!!!!!! 代码抄错了!!!!!
这锅只能给notepad++了,编码时关键字自动补齐!!!
memcmp和memcpy你们不觉得这两个函数很像吗????
结论:
段错误原因:
野指针造成的无法free!!!!
轻松! 无语! 以后编码还是要边写边调试,出错太浪费时间了!!!
附上两个函数小度的解释:
memcpy
memcpy指的是C和C++使用的内存拷贝函数,函数原型为void *memcpy(void *destin, void *source, unsigned n);函数的功能是从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中,即从源source中拷贝n个字节到目标destin中。
memcmp
memcmp函数的原型为 int memcmp(const void *str1, const void *str2, size_t n));其功能是把存储区 str1 和存储区 str2 的前 n 个字节进行比较。该函数是按字节比较的,位于string.h。