递归函数的分段失败

Posted

技术标签:

【中文标题】递归函数的分段失败【英文标题】:Segmentation Failure with recursion function 【发布时间】:2020-09-12 18:08:30 【问题描述】:

我将编写 4 way-merge-sort 并且我得到分段失败作为错误。

我觉得这与递归深度无关,因为它为我的测试用例递归调用了 20 次。

当我把mergesort_4_way_rec中的merge_4_way去掉后,就可以流畅运行了。我可以找出导致错误的 merge_4_way 的任何功能设计问题。我使用 clang 进行编译,使用 MacOS 进行测试。

#include <stdio.h>
#include "merge_sort.h"

// merge the four sub-array
// [low, mid1), [mid1, mid2), [mid2, mid3), [mid3, high)
void merge_4_way(int* array, int low, int mid1, int mid2, int mid3, int high) 
    int i = low, j = mid1, k = mid2, p = mid3, l = low;

    int destArray[high - low];
    
    while((i < mid1) && (j < mid2) && (k < mid3) && (p < high))  
      
        if( (array[i] <= array[j]) && (array[i] <= array[k]) && (array[i] <= array[p])) 
        
            destArray[l++] = array[i++]; 
        
        else if ((array[j] <= array[i]) && (array[j] <= array[k]) && (array[j] <= array[p]))
        
            destArray[l++] = array[j++]; 
        
        else if ((array[k] <= array[i]) && (array[k] <= array[j]) && (array[k] <= array[p]))
        
            destArray[l++] = array[k++];
        
        else if ((array[p] <= array[i]) && (array[p] <= array[j]) && (array[p] <= array[k]))
        
            destArray[l++] = array[p++];
        
    


    // case where first and second ranges 
    // have remaining values  
    while ((i < mid1) && (j < mid2) && (k < mid3))  
      
        if((array[i] <= array[j]) && (array[i] <= array[k])) 
         
            destArray[l++] = array[i++]; 
         
        else if ((array[j] <= array[i]) && (array[j] <= array[k])) 
         
            destArray[l++] = array[j++]; 
        
        else if ((array[k] <= array[i]) && (array[k] <= array[j])) 
         
            destArray[l++] = array[k++]; 
          

     
  
    // case where first and second ranges 
    // have remaining values  
    while ((i < mid1) && (j < mid2) && (p < high))  
      
        if((array[i] <= array[j]) && (array[i] <= array[p])) 
         
            destArray[l++] = array[i++]; 
         
        else if ((array[j] <= array[i]) && (array[j] <= array[p])) 
         
            destArray[l++] = array[j++]; 
        
        else if ((array[p] <= array[i]) && (array[p] <= array[j])) 
         
            destArray[l++] = array[p++]; 
          

    

    while ((i < mid1) && (k < mid3) && (p < high))  
      
        if((array[i] <= array[k]) && (array[i] <= array[p])) 
         
            destArray[l++] = array[i++]; 
         
        else if ((array[k] <= array[i]) && (array[k] <= array[p])) 
         
            destArray[l++] = array[k++]; 
        
        else if ((array[p] <= array[i]) && (array[p] <= array[k])) 
         
            destArray[l++] = array[p++]; 
          

     

    while ((j < mid2) && (k < mid3) && (p < high))  
      
        if((array[j] <= array[k]) && (array[j] <= array[p])) 
         
            destArray[l++] = array[j++]; 
         
        else if ((array[k] <= array[i]) && (array[k] <= array[p])) 
         
            destArray[l++] = array[k++]; 
        
        else if ((array[p] <= array[j]) && (array[p] <= array[k])) 
         
            destArray[l++] = array[p++]; 
          

    

    // case where first and second ranges 
    // have remaining values  
    while ((i < mid1) && (j < mid2))  
      
        if(array[i] <= array[j]) 
         
            destArray[l++] = array[i++]; 
         
        else
         
            destArray[l++] = array[j++]; 
         
      
  
    // case where second and third ranges 
    // have remaining values  
    while ((j < mid2) && (k < high))  
      
        if(array[j] <= array[k]) 
         
            destArray[l++] = array[j++]; 
         
        else
         
            destArray[l++] = array[k++]; 
          
        

    // case where third and forth ranges 
    // have remaining values  
    while ((k < mid3) && (p < high))  
      
        if(array[k] <= array[p]) 
         
            destArray[l++] = array[k++]; 
         
        else
         
            destArray[l++] = array[p++]; 
          
      
  
    // case where first and third ranges have  
    // remaining values  
    while ((i < mid1) && (k < mid3))  
      
        if(array[i] <= array[k]) 
         
            destArray[l++] = array[i++]; 
         
        else
         
            destArray[l++] = array[k++]; 
          
    
    // case where first and forth ranges have  
    // remaining values  
    while ((i < mid1) && (p < high))  
      
        if(array[i] <= array[p]) 
         
            destArray[l++] = array[i++]; 
         
        else
         
            destArray[l++] = array[p++]; 
          
     
    // case where second and forth ranges have  
    // remaining values  
    while ((j < mid2) && (p < high))  
      
        if(array[j] <= array[p]) 
         
            destArray[l++] = array[j++]; 
         
        else
         
            destArray[l++] = array[p++]; 
          
    

    // copy remaining values from the first range  
    while (i < mid1)  
        destArray[l++] = array[i++];  
  
    // copy remaining values from the second range  
    while (j < mid2)  
        destArray[l++] = array[j++];  
  
    // copy remaining values from the third range  
    while (k < mid3)  
        destArray[l++] = array[k++];
    
    // copy remaining values from the third range  
    while (p < high)  
        destArray[l++] = array[p++];

    for (int i = low; i < high; i++)
        array[i] = destArray[i];
     
    


// divide the array [low, high) into 4 parts (roughly same size).
// For each part, if # of items > 3, recursively call mergesort_4_way_rec; 
// Otherwise sort them as you like
// Finally use merge_4_way merge them
void mergesort_4_way_rec(int* array, int low, int high) 
    int mid1 = low + ((high-low) / 4);
    int mid2 = low + 2 * ((high-low) / 4);
    int mid3 = low + 3 * ((high-low) / 4);
     printf("%d\n", (high - low));
    if ((high - low) > 3)

        mergesort_4_way_rec(array, low, mid1);
        mergesort_4_way_rec(array, mid1, mid2);
        mergesort_4_way_rec(array, mid2, mid3);
        mergesort_4_way_rec(array, mid3, high);

        merge_4_way(array, low, mid1, mid2, mid3, high);
     else 
        for(int i=low; i<high;i++)
        
            for(int j=low;j<high-i-1;j++)
            
                if(array[j]<array[j+1])
                
                    int temp=array[j];
                    array[j]=array[j+1];
                    array[j+1]=temp;
                
            
         
        return;  
    


    

【问题讨论】:

请尝试使用clang address sanitizer 或valgrind。可悲的是,SO 没有提供机器人建议“你尝试过 valgrind/clang sanitizer 吗?”用于 C/C++ 代码。 调试器是你的朋友。如果它是minimal reproducible example,我们中的一个人可能会为您运行它并可能发现问题。 【参考方案1】:

如果low 不是0,那么您最终会写到destArray 的末尾。

destArray 的大小为high - low。您将l(写入destArray 时使用的索引变量)初始化为low,因此您最终写入元素destArray[low]destArray[high - 1],如果不是low,这将超过结尾零。

一种解决方案是初始化l = 0;,并将merge_4_way末尾的循环更改为正确索引destArray

另一种解决方案是声明destArray 为所有元素(甚至是未使用的元素)提供空间,int destArray[high]; 尽管这会浪费堆栈空间。

【讨论】:

以上是关于递归函数的分段失败的主要内容,如果未能解决你的问题,请参考以下文章

奇/偶递归函数中的分段错误

iverilog递归函数导致分段错误

matlab分段+递归函数的表示方法

在递归函数上使用 unordered_map unordered_set 的分段错误

C函数调用分段错误[重复]

SQL Server 在生产服务器上的函数之间的递归调用失败