C - 函数调用本身导致递归调用中的段错误

C - Function call itself causes segfault in recursive calls

我遇到了一个我无法理解的段错误:我有一个递归函数,它扩展了一个表示像素的数组:从一个索引开始,它围绕索引展开,通过调用相同的函数来创建像素组左右上下(又名索引 -1,索引 +1 ...)。出于调试目的,我在函数的第一行有一个 printf 调用,并且在 4 个递归调用中的每一个之前都有一个调用。我没有得到的是,在递归调用本身的递归过程中,我最终遇到了段错误(我得到的是调用之前的打印,而不是函数开始时的打印)。

void explore(Pixel * array, int j, Tache * cur_pound, int * it, Pixel previous){
    printf("%d\n", j); // I DONT GET THIS PRINT AT LAST RECURSIVE CALL
    // out of bounds
    if(j > sizeX * sizeY)
        return;

    // allready explored index
    if(array[j].explored == 1){
        return;
    }

   // to big of a color difference between this pixel and the reference one
   if(abs((int)array[j].r - previous.r) > SEUIL || abs((int)array[j].g - previous.g) > SEUIL || abs((int)array[j].b - previous.b) > SEUIL){
      return;
   }

   array[j].explored = 1;
   cur_pound->limits[* it] = j;
   (* it)++;

  // recursion
  if(j +1 < sizeX * sizeY && array[j+1].explored != 1){
        printf("before SF\n); // I GET THIS PRINTF
        explore(array, j + 1, cur_pound, it, previous);
  }
  // 3 other recursive calls removed for simplicity here
}

关于我的数据结构:Tache *struct,包含 3 个 GLubytes 和 limitsint * 代表属于该组的每个像素索引。 Pixel 包含 3 个 GLubytes 和一个 char 表示该像素是否已被该函数访问过。作为第一个参数提供给函数的 array 是代表我的图像的 Pixel 数组。 it 是一个 int 表示组中的索引,以便我的函数知道它应该在数组的哪个位置添加新索引。 Limits 在此函数外的 -1 处初始化,并分配有 malloc(size * sizeof(int)),其中 size 是图像的宽度乘以其高度。

初始调用是这样完成的:

void taches_de_couleur(Image *i){
    int j, k, y, size, it;
    GLubyte * im;
    Pixel * array;

    sizeX = i->sizeX;
    sizeY = i->sizeY;  
    k = 0;
    size = sizeX * sizeY;
    array = malloc(size * sizeof(Pixel));
    im = i->data;

   /* build the array from image data */
   for(j = 0; j < 3 * size; j+= 3){
       array[k].explored = 0;
       array[k].r = i->data[j];
       array[k].g = i->data[j + 1];
       array[k].b = i->data[j + 2];
       k++;
    }

    Tache * new_pound;
    new_pound = malloc(sizeof(Tache));
    new_pound->limits = malloc(size * sizeof(int));
    int x= 0;
    while(x < size){
        new_pound->limits[x] = -1;
        x++;
    }
    it = 0;
    explore(array, 0, new_pound, &it, array[0]);
}

请注意,该程序在处理小图像时不会产生任何 SF(我能做的最大图像是 512x384 像素)。 这件事让我头疼了一个星期,无法弄清楚是什么导致了这个段错误,这就是为什么我问你们是否可以在这里看到任何明显的东西。如果需要,我可以添加第二个调用 explore 的函数,但这部分似乎还不错。

编辑:这是当我 运行 图像太大时 gdb 给我的输出:

Thread 1 "palette" received signal SIGSEGV, Segmentation fault.
0x00007ffff7b730be in __GI___libc_write (fd=1, buf=0x555555592770, 
nbytes=7)
at ../sysdeps/unix/sysv/linux/write.c:26
26  ../sysdeps/unix/sysv/linux/write.c: No such file or directory.

编辑:由于我未能提供足够的资源,请参阅 https://github.com/BruhP8/TachesDeCouleur 了解完整项目 提前致谢

您的代码中的第一个边界检查似乎应该是:

if( j >= sizeX * sizeY )

而不是

if( j > sizeX * sizeY )

(因为数组的最后一个元素是数组[size - 1] 而不是数组[size])

What I dont get is, I end up with a segfault during the recursion at the recrusive call itself (I get the print that is just before the call, but not the one at function start).

这几乎是堆栈耗尽的标志。

运行 您的程序在调试器下,并检查导致段错误的 指令 。很有可能,它将是堆栈操作指令(CALLPUSH)之一,或者是堆栈递减后的堆栈取消引用指令。您还可以查看 $SP 寄存器的值,并将其与堆栈段的边界进行比较(如果您在 Linux 上,则来自 /proc/$pid/maps)。

您显示的代码似乎没有分配任何堆栈,因此问题很可能出在您省略的代码中。

Note that the program does not produce any SF when working with small images

这是另一个迹象:您可能正在堆栈上分配一个新图像,图像越大,您可以实现的递归级别就越少。

P.S。在 Linux 上,默认堆栈大小通常为 8MiB。尝试 ulimit -s unlimited —— 如果这允许程序更深入地重复,那将是我的猜测正确的明确标志。但不要使用 ulimit -s unlimited 作为修复(它不是)。

更新:

使用完整的源代码,我能够构建 palette 程序。每次对 explore 的递归调用仅占用 48 个字节的堆栈(并不多)。

但是使用默认的 8MiB 堆栈,这将总递归限制在 (8 << 20) / 48 == 174762 层深。

TL;DR:如果您的递归过程需要每个像素 一级递归 ,那么您将无法处理大图像。您必须重写该过程以改为迭代。