C - сам вызов функции вызывает segfault в рекурсивных вызовах - PullRequest
0 голосов
/ 04 апреля 2020

Я борюсь с ошибкой segfault, которую не могу понять: у меня есть рекурсивная функция, которая расширяет массив, представляющий пиксели: начиная с индекса, он расширяется вокруг индекса, создавая группы пикселей, вызывая ту же функцию слева направо вверх и вниз (он же индекс -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 *, который представляет каждый индекс пикселей, принадлежащий этой группе. A 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 при работе с небольшими изображениями (самое большое, что я мог сделать, было 512x384px). Эта штука вызывает у меня головную боль в течение недели, я не могу понять, что является причиной этого segfault, и поэтому я спрашиваю вас, ребята, видите ли вы что-нибудь очевидное здесь. Я могу добавить вторую функцию, которая вызывает исследовать, если это необходимо, но эта часть кажется хорошей.

РЕДАКТИРОВАТЬ: это вывод, который дает мне 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 для полного проекта Спасибо заранее

Ответы [ 2 ]

1 голос
/ 04 апреля 2020

Что я не получаю, так это то, что во время рекурсии у меня возникает ошибка по умолчанию при рекурсии (я получаю распечатку, которая находится непосредственно перед вызовом, но не ту, что при запуске функции).

Это почти верный признак исчерпания стека.

Запустите вашу программу под отладчиком и изучите инструкцию , которая вызывает segfault. Скорее всего, это будет одна из инструкций по обработке стека (CALL, PUSH) или инструкция разыменования стека, которая следует за уменьшением стека. Вы также можете посмотреть значение регистра $SP и сравнить его с границами сегмента стека (с /proc/$pid/maps, если вы используете Linux).

Код, который вы показали, не похоже, что выделяет какой-либо стек, поэтому проблема, скорее всего, в пропущенном коде.

Обратите внимание, что программа не создает SF при работе с маленькими изображениями

Это это еще один признак: вы, вероятно, размещаете новое изображение в стеке, и чем больше изображение, тем меньше уровней рекурсии вы можете достичь.

PS При Linux размер стека по умолчанию часто составляет 8 МБ. Попробуйте ulimit -s unlimited - если это позволит программе повториться глубже, это будет верным признаком того, что мое предположение верно. Но не используйте ulimit -s unlimited в качестве исправления (это не так).

Обновление:

С полным исходным кодом я смог собрать palette программа. Каждый рекурсивный вызов explore занимает только 48 байт стека (что не так много).

Но при использовании стека по умолчанию 8 МБ это ограничивает общую рекурсию до (8 << 20) / 48 == 174762 уровней глубиной.

TL; DR: если ваша рекурсивная процедура требует один уровень рекурсии на пиксель , то вы не сможете обрабатывать большие изображения. Вместо этого вы должны переписать процедуру итеративно.

0 голосов
/ 04 апреля 2020

Кажется, что первая проверка границ в вашем коде должна быть:

if( j >= sizeX * sizeY )

, а не

if( j > sizeX * sizeY )

(поскольку последний элемент вашего массива - это массив [размер - 1] а не массив [размер])

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...