一个由快速排序引发的段错误

  今天在实现一个查找功能的时候,需要用到排序,因为用的是C开发,就把自己之前写的快速排序算法直接拿来用了。代码如下:

 1 static void QuickSort(ElemType *array,int left,int right)
 2 {
 3     if(left > right)
 4     {
 5         return;
 6     }
 7     int i,j;
 8     ElemType temp, base;
 9     base = array[left];
10     i = left;
11     j = right;
12     while(i < j)
13     {
14         //顺序很重要,要先从右边开始找
15         while(array[j] >= base && j > i)
16         {
17             j--;
18         }
19         array[i] = array[j];
20         while(array[i] <= base && i < j)
21         {
22             i++;
23         }
24         array[j] = array[i];
25     }
26     //最终将基准数归位
27     array[i] = base;
28     QuickSort(array,left,i-1);//继续处理左边的,这里是一个递归的过程
29     QuickSort(array,i+1,right);//继续处理右边的 ,这里是一个递归的过程
30 }

  编译->运行,出现的结果是:Segmentation fault(core dumped)。看到这个报错,我首先想到的是数组越界了,可是不管怎么检查,也没有找到越界的地方。

  实在是找不出错误来,于是我就干脆把快排重新写一遍,编译运行成功了。然后一对比,开始我以为我找到原因了,是在代码的第3行:if(left < right)应为if(left <= right),这的确是一个错误,但是却不是导致段错误的原因。因为,即便是没有‘=’,程序也会运行下去的。也就是说,若出现left == right,则在代码的28行这个新的递归中,是会返回来从而结束掉递归的。

  到底是哪里有问题?我分享了代码的很多处都没有找到原因,但是,我注意到一个细节:当我传入的数组的元素为5的时候,是会出现Segmentation fault(core dumped)错误,但是当我传入的数组元素为12的时候,却顺利运行。以这一点为突破口,我将传入的数组元素设为了5,然后使用了gdb调试。

  传入的原始数组为 83 86 77 15 93,下面是排序的过程:

  1. 第一次递归调用后,数组就已经拍好了:15 77 83 86 93,且此时基准数base的位置i为2,left为0。接着进入下一个递归Quick(array,left,i-1);
  2. 新的递归中left = 0,right = 1,经过13~25行的排序后,基准数base的位置i为0,left还是为0。程序进入下一个递归Quick(array,left,i-1),注意到递归中传入的第三个参数为i-1,而此时i = 0,所以,递归调用传入的第三个参数实际上是负值。在我的计算机中为4294967295,这个值也是新的递归中right的值。

  但是,导致Segmentation fault(core dumped)错误的原因,真的是简单的数组越界吗?我仔细思考了一下觉得有两个原因:

  1. 不断递归导致的入栈过程,最终导致栈溢出;
  2. 数组越界;

  乍一看这里应该是情形2的,因为传入的参数是一个越界值。但是,事实上我使用单步调试的时候,在引用array[4294967295]的时候并没有报错。最终的报错的位置为”Quick(array=0x8048034,left = 100,right=17“,从这一句基本结合上面的越界未报错,基本可以判定应该是栈溢出了。

  但是这样的判断还是错的。我写了下面的测试程序:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3
 4 static void test(int a);
 5
 6 int main(void)
 7 {
 8     test(0);
 9     return 0;
10 }
11
12 static void test(int a)
13 {
14     int b;
15     b = ++a;
16     printf("This is the %dth recursion.\n",b);
17     test(b);
18 }

  实验结果显示,直到174544次递归调用,才出现了栈溢出的现象。而我统计快排序中的递归次数,发现在第三次就出现段错误了,这到底是怎么回事?

  最终修改过后的程序如下:

 1 static void QuickSort(ElemType *array,int left,int right)
 2 {
 3     if(left >= right)
 4     {
 5         return;
 6     }
 7     int i,j;
 8     ElemType temp, base;
 9     base = array[left];
10     i = left;
11     j = right;
12     while(i < j)
13     {
14         //顺序很重要,要先从右边开始找
15         while(array[j] >= base && j > i)
16         {
17             j--;
18         }
19         if(i < j)
20         {
21             array[i++] = array[j];
22         }
23
24         while(array[i] <= base && i < j)
25         {
26             i++;
27         }
28          if(i < j)
29         {
30             array[j--] = array[i];
31         }
32     }
33     //最终将基准数归位
34     array[i] = base;
35     if(i == 0)
36     {
37         return;
38     }
39     QuickSort(array,left,i-1);//继续处理左边的,这里是一个递归的过程
40     QuickSort(array,i+1,right);//继续处理右边的 ,这里是一个递归的过程
41 }

  虽然问题解决了,但是我还是不知道确切的出错原因,希望知道答案的大牛能解答一下。

时间: 2024-09-30 20:40:45

一个由快速排序引发的段错误的相关文章

由malloc和new引发的段错误

class Queue{ private: struct node{ string data; struct node * next,*priv; } private: struct node * pthread; } 当我给struct node 分配一块新内存时 struct node * pnew pnew = (struct node *)malloc(sizeof(struct node)); 此时应用pnew指针就会出现段错误,而在编译的时候是没有办法检测出来的. 正确应用是 str

你的java/c/c++程序崩溃了?揭秘段错误(Segmentation fault)(3)

前言 接上两篇: 你的C/C++程序为什么无法运行?揭秘Segmentation fault (1) 你的C/C++程序为什么无法运行?揭秘Segmentation fault (2) 写到这里,越跟,越发现真的是内核上很白,非一般的白. 但是既然是研究,就定住心,把段错误搞到清楚明白. 本篇将作为终篇,来结束这个系列,也算是对段错误和程序调试.寻找崩溃原因(通常不会给你那么完美的stackstrace和人性化的错误提示)的再深入. 本篇使用到的工具或命令: dmesg strace gdb l

一个多线程问题引发的血案-(代码段执行完毕,子进程未执行完毕导致段错误)

今天遇到一个问题,gdb执行程序完全没有问题,但直接执行就会段错误,百思不得其解,各种纠结,各种搜索引擎都试了一遍,无果!后来问题还是被我自己挖出来了. 看下边一段代码: int TaskSendControl() { pthread_t prov_thread[CLIENT_NUM]; int prov[CLIENT_NUM]; for(int i=0; i< CLIENT_NUM; i++) { prov[i] = i; if( pthread_create(&prov_thread[i

Error-ASP.NET:由于未能找到 id 为“FileUpload1$gvFiles$ctl02$lnkBtnRemoveFile”的控件或在回发后将同一 ID 分配给另一个控件,导致发生错误。如果未分配 ID,请显式设置引发回发事件的控件的 ID 属性以避免此错误。

ylbtech-Error-ASP.NET:由于未能找到 id 为“FileUpload1$gvFiles$ctl02$lnkBtnRemoveFile”的控件或在回发后将同一 ID 分配给另一个控件,导致发生错误.如果未分配 ID,请显式设置引发回发事件的控件的 ID 属性以避免此错误. 1.返回顶部 1. “/”应用程序中的服务器错误. 由于未能找到 id 为“FileUpload1$gvFiles$ctl02$lnkBtnRemoveFile”的控件或在回发后将同一 ID 分配给另一个控件

段错误调试神器 - Core Dump详解

一.前言: 有的程序可以通过编译, 但在运行时会出现Segment fault(段错误). 这通常都是指针错误引起的. 但这不像编译错误一样会提示到文件某一行, 而是没有任何信息, 使得我们的调试变得困难起来. gdb: 有一种办法是, 我们用gdb的step, 一步一步寻找. 这放在短小的代码中是可行的, 但要让你step一个上万行的代码, 我想你会从此厌恶程序员这个名字, 而把他叫做调试员. 我们还有更好的办法, 这就是core file. ulimit: 如果想让系统在信号中断造成的错误时

linux下转格式函数iconv段错误

今天将windows代码移植到Linux下,其中用到了Unicode转char的函数,被坑了一会,相关函数及编码格式,Linux与windows不同,有几点需要注意: 1.wchar_t 在Linux下占用4个字节,在windows下占2个字节: 2.Linux默认的文本编码方式是UTF-8:Linux终端汉字显示的设置方式:vi /etc/sysconfig/i18n: 设置LANG="en_US.UTF-8"或者LANG="zh_CN.UTF-8": 3.ico

【转】段错误调试神器 - Core Dump详解

from:http://www.embeddedlinux.org.cn/html/jishuzixun/201307/08-2594.html 段错误调试神器 - Core Dump详解 来源:互联网 作者:Alex 时间:2013-07-08 Tag:Linux   点击: 11670 一.前言: 有的程序可以通过编译, 但在运行时会出现Segment fault(段错误). 这通常都是指针错误引起的. 但这不像编译错误一样会提示到文件某一行, 而是没有任何信息, 使得我们的调试变得困难起来

Linux环境下段错误的产生原因及调试方法小结(转)

最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且 项目工程庞大复杂,出现了不少问题,其中遇到最多.花费时间最长的问题就是著名的“段错误”(Segmentation Fault).借此机会系统学习了一下,这里对Linux环境下的段错误做个小结,方便以后同类问题的排查与解决. 1. 段错误是什么 一句话来说,段错误是指访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址.访问了系统保护的内存地址.访问了只读的内存地址等等情况.这里贴一个对于“段

Linux环境下段错误的产生原因及调试方法小结

最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且项目工程庞大复杂,出现了不少问题,其中遇到最多.花费时间最长的问题就是著名的“段错误”(Segmentation Fault).借此机会系统学习了一下,这里对Linux环境下的段错误做个小结,方便以后同类问题的排查与解决. 1. 段错误是什么 一句话来说,段错误是指访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址.访问了系统保护的内存地址.访问了只读的内存地址等等情况.这里贴一个对于“段错