'C: Heap-sort (percolate) error: abort, where is the error? / How to get more details about the error?

i tried to implement a simple heap sort algorithm, but it always terminates with the message [1] 25535 abort ./build/maps. I ran lldb ./build/maps core to inspect the error, this is what I got:

stop reason = signal SIGABRT
    frame #0: 0x00000001b65219b8 libsystem_kernel.dylib`__pthread_kill + 8
libsystem_kernel.dylib`__pthread_kill:
->  0x1b65219b8 <+8>:  b.lo   0x1b65219d8               ; <+40>
    0x1b65219bc <+12>: pacibsp
    0x1b65219c0 <+16>: stp    x29, x30, [sp, #-0x10]!
    0x1b65219c4 <+20>: mov    x29, sp
Target 0: (maps) stopped.

i don't know what to do with this. I suspect, that there is somewhere a overflow, because if I call the function with the length of the array - 1 it works. If i run lldb step by step I can't find the error, it just works til the end, what really confuses me However here is my Code:

#include <stdio.h>
#include <stdlib.h>
#include "./collections/heap.h"

int compare(const void *element_1, const void *element_2) {
    const int *a = element_1;
    const int *b = element_2;
    return *a - *b;
}

int main(int argc, char **argv) {
    int array[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    heap_sort(array, 9, sizeof(array[0]), compare);
    for(int i = 0; i < 9; i++) {
        printf("%d\n", array[i]);
    }
}
#include "heap.h"
#include "../assert.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>

static void percolate(void *, int, size_t, size_t, int (*compare)(const void *, const void *));
static void swap(void * , int, int, size_t);


void heap_sort(void *array, size_t n_elements, size_t size_per_element, int (*compare)(const void *, const void *)) {
    int len = n_elements * size_per_element; // array has `len` bytes.
    unsigned char *src = array;
    for(int i = len/2; i >= 0; i -= size_per_element) {
        percolate(array, i, len - size_per_element, size_per_element, compare);
    }
    printf("successfully ran first for loop\n");
    for(int i = len; i > 0; i -= size_per_element) {
        swap(array, 0, i, size_per_element);
        percolate(array, 0, i - size_per_element, size_per_element, compare);
    }
    printf("successfully ran second for loop\n");
}

/**
 * Swaps the variables at index i and index j.
 */
static void swap(void *array, int i, int j, size_t element_size) {
    unsigned char *src = array;
    while(element_size-- > 0) {
        src[i] ^= src[j];
        src[j] ^= src[i];
        src[i] ^= src[j];
        ++i;
        ++j;
    }
}

static void percolate(void *array, int current, size_t last, size_t element_size, int (*compare)(const void *, const void *)) {
    unsigned char *src = array;
    int i = current;
    int j;
    while((2*i) + element_size <= last) {
        j = (2*i) + element_size;
        if(j+ element_size <= last) {
            if(compare(&src[j], &src[j+element_size]) > 0) {
                j += element_size;
            }
        }
        if(compare(&src[i], &src[j]) > 0) {
            swap(array, i, j, element_size);
            i = j;
        } else {
            break;
        }
    }
}

Thank you



Solution 1:[1]

You are exceeding the bounds of the array. This is UB (undefined behavior).

On your system, this produces a crash. On my system, it just produces bad results.

In addition to gdb/lldb debug, you can add range checks and debug printf

Here's a refactored version (compile with -DDEBUG). It checks all array indexes for being within range. You can add a debugger breakpoint on the _sysfault function to use both debug methods.

Anyway, here is the code:

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>

#ifdef DEBUG
#define dbgprt(_fmt...)     printf(_fmt)
#define CHKI(_idx)          chki(_idx,__LINE__)
#else
#define CHKI(_idx)          do { } while (0)
#define dbgprt(_fmt...)     do { } while (0)
#endif

#define CHKB(_off)          CHKI((_off) / sizeof(bigarray[0]))

#define ARRCNT(_arr) \
    (sizeof(_arr) / sizeof(_arr[0]))

#define RNGEM1(_val,_lo,_hi) \
    (((_val) >= (_lo)) && ((_val) < (_hi)))

int bigarray[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

#define sysfault(_fmt...) \
    do { \
        printf(_fmt); \
        _sysfault(); \
    } while (0)

void
_sysfault(void)
{

    printf("sysfault: aborting ...\n");
    exit(1);
}

void
chki(int idx,int lno)
{

    if (! RNGEM1(idx,0,ARRCNT(bigarray)))
        sysfault("chki: range error line %d -- idx=%d\n",lno,idx);
}

/**
 * Swaps the variables at index i and index j.
 */
static void
swap(void *array, int i, int j, size_t element_size)
{
    unsigned char *src = array;

    dbgprt("swap: ENTER i=%d j=%d element_size=%d\n",i,j,element_size);

    CHKB(i);
    CHKB(j);

    while (element_size-- > 0) {
        src[i] ^= src[j];
        src[j] ^= src[i];
        src[i] ^= src[j];
        ++i;
        ++j;
    }

    dbgprt("swap: EXIT\n");
}

static void
percolate(void *array, int current, size_t last, size_t element_size,
    int (*compare)(const void *,const void *))
{
    unsigned char *src = array;
    int i = current;
    int j;

    dbgprt("percolate: ENTER current=%d last=%zu element_size=%zu\n",
        current,last,element_size);

    while ((2 * i) + element_size <= last) {
        j = (2 * i) + element_size;

        dbgprt("percolate: LOOP i=%d j=%d\n",i,j);
        CHKB(i);
        CHKB(j);

        if (j + element_size <= last) {
            if (compare(&src[j], &src[j + element_size]) > 0) {
                dbgprt("percolate: BEF\n");
                j += element_size;
            }
        }
        if (compare(&src[i], &src[j]) > 0) {
            dbgprt("percolate: AFT\n");
            swap(array, i, j, element_size);
            i = j;
        }
        else {
            dbgprt("percolate: STOP\n");
            break;
        }
    }

    dbgprt("percolate: EXIT\n");
}

void
heap_sort(void *array, size_t n_elements, size_t size_per_element,
    int (*compare)(const void *, const void *))
{
    int len = n_elements * size_per_element;    // array has `len` bytes.
    unsigned char *src = array;
    int i;

    i = len / 2;
    for (; i >= 0; i -= size_per_element)
        percolate(array, i, len - size_per_element, size_per_element, compare);
    printf("successfully ran first for loop\n");

#if 1
    i = len;
#else
    i = len - 1;
#endif
    for (; i > 0; i -= size_per_element) {
        swap(array, 0, i, size_per_element);
        percolate(array, 0, i - size_per_element, size_per_element, compare);
    }
    printf("successfully ran second for loop\n");
}

int
compare(const void *element_1, const void *element_2)
{
    const int *a = element_1;
    const int *b = element_2;

    return *a - *b;
}

int
main(int argc, char **argv)
{

    setlinebuf(stdout);

    heap_sort(bigarray, ARRCNT(bigarray), sizeof(bigarray[0]), compare);

    for (int i = 0; i < ARRCNT(bigarray); i++) {
        printf("%d\n", bigarray[i]);
    }

    return 0;
}

Here is the program output:

percolate: ENTER current=18 last=32 element_size=4
percolate: EXIT
percolate: ENTER current=14 last=32 element_size=4
percolate: LOOP i=14 j=32
percolate: AFT
swap: ENTER i=14 j=32 element_size=4
swap: EXIT
percolate: EXIT
percolate: ENTER current=10 last=32 element_size=4
percolate: LOOP i=10 j=24
percolate: AFT
swap: ENTER i=10 j=24 element_size=4
swap: EXIT
percolate: EXIT
percolate: ENTER current=6 last=32 element_size=4
percolate: LOOP i=6 j=16
percolate: AFT
swap: ENTER i=6 j=16 element_size=4
swap: EXIT
percolate: EXIT
percolate: ENTER current=2 last=32 element_size=4
percolate: LOOP i=2 j=8
percolate: STOP
percolate: EXIT
successfully ran first for loop
swap: ENTER i=0 j=36 element_size=4
chki: range error line 59 -- idx=9
sysfault: aborting ...

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Craig Estey