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 和 limits
,int *
代表属于该组的每个像素索引。 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).
这几乎是堆栈耗尽的标志。
运行 您的程序在调试器下,并检查导致段错误的 指令 。很有可能,它将是堆栈操作指令(CALL
、PUSH
)之一,或者是堆栈递减后的堆栈取消引用指令。您还可以查看 $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:如果您的递归过程需要每个像素 一级递归 ,那么您将无法处理大图像。您必须重写该过程以改为迭代。
我遇到了一个我无法理解的段错误:我有一个递归函数,它扩展了一个表示像素的数组:从一个索引开始,它围绕索引展开,通过调用相同的函数来创建像素组左右上下(又名索引 -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 和 limits
,int *
代表属于该组的每个像素索引。 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).
这几乎是堆栈耗尽的标志。
运行 您的程序在调试器下,并检查导致段错误的 指令 。很有可能,它将是堆栈操作指令(CALL
、PUSH
)之一,或者是堆栈递减后的堆栈取消引用指令。您还可以查看 $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:如果您的递归过程需要每个像素 一级递归 ,那么您将无法处理大图像。您必须重写该过程以改为迭代。