0

I am VERY new to C, but I thought that I'd learn it while I'm learning basic data structures. Anyway, I'm having a problem wrapping my head about how/where errors are coming up in my code.

Basically, I'm getting two different kinds of errors:

  1. Segmentation Faults (@ binary heap length 2 and 3) when subtracting from heap.
  2. Malloc/Realloc errors when I add to binary heap enough to make it to length 4 (and beyond), and then subtract to length 2 (I get a non-valid binary heap structure @ length 3 when I do this as well).

Basically, I just want to see what exactly I'm doing wrong to get this behavior. Also, if there's anything in my code that is downright appaling, I'd like to know that too.

So, here's my code:

void printArray(int array[], int size) {
    printf("[");
    for (int i = 0; i < size; i++) {
        if (i == (size - 1)) {
            printf("%d", array[i]);
        } else {
            printf("%d, ", array[i]);
        }
    }
    printf("]\n");
}

int getLeftChild(int h_array[], int p_index, int size) {
/* Summary: Obtains the `left child` of Parent at given parent index (p_index)
 * 
 * Input: `h_array` - The binary heap
 *        `p_index` - The index of the parent that we are currently looking at
 *        `size` - The size of the binary heap.
 *
 * Return: `0` if the index given points out of bounds of the array. Returns the child of parent at p_index if not
 */ 
    int child = 0;
    if (p_index * 2 + 1 < size) {
        child = h_array[p_index * 2 + 1];
    }
    return child;
}

int getRightChild(int h_array[], int p_index, int size) {
/* Summary: Obtains the `right child` of Parent at given parent index (p_index)
 * 
 * Input: `h_array` - The binary heap
 *        `p_index` - The index of the parent that we are currently looking at
 *        `size` - The size of the binary heap.
 *
 * Return: `0` if the index given points out of bounds of the array. Returns the child of parent at p_index if not
 */ 
    int child = 0;
    if ((p_index * 2 + 2) < size) {
        child = h_array[p_index * 2 + 2];
    }
    return child;
}

void heapSort(int h_array[], int size, int min_max) {
/* Summary: Performs a heap sort on a binary heap array; parents with 2 children maximum.
 *          This could be used to implement a priority queue, as the node with the highest (or lowest)
 *          priority will be at the root of the list.
 * Input: `h_array` - the heap array to sort
 *        `size` - The size of the heap array
 *        `min_max` - an input that will tell whether or not we want to return a 'maxed', or a 'min'd' binary heap.
 *                      maxed will have highest priority at the root, and min'd will have the lowest priority at the root
 * 
 * Returns: Does not return. Performs all sorting operations on input array.
 **/
    int parent, leftChild, rightChild, p_holder, i = 0;
    while (i < (size / 2)) {
        parent = h_array[i];
        leftChild = getLeftChild(h_array, i, size);
        rightChild = getRightChild(h_array, i, size);

        if (min_max == 0 ) {
            while (parent < leftChild || parent < rightChild) {
                p_holder = parent;
                if (parent < leftChild) {
                    h_array[i] = leftChild;
                    h_array[(i * 2) + 1] = p_holder;
                } else if (parent < rightChild) {
                    h_array[i] = rightChild;
                    h_array[(i * 2) + 2] = p_holder;
                }
                i = 0;
                parent = h_array[i];
                leftChild = getLeftChild(h_array, i, size);
                rightChild = getRightChild(h_array, i, size);
            }
        i++;
        } else {
            while ((leftChild != 0 && parent > leftChild) || (rightChild != 0 &&parent > rightChild)) {
                p_holder = parent;
                if ((leftChild != 0) && parent > leftChild) {
                    h_array[i] = leftChild;
                    h_array[(i * 2) + 1] = p_holder;
                } else if ((rightChild != 0) && parent > rightChild) {
                    h_array[i] = rightChild;
                    h_array[(i * 2) + 2] = p_holder;
                }
                i = 0;
                parent = h_array[i];
                leftChild = getLeftChild(h_array, i, size);
                rightChild = getRightChild(h_array, i, size);
            }
        i++;
        }
    }
}

void heapAdd(int h_array[], int *a_size, int value, int *min_max_ptr) {
/* Summary: Adds a value to the binary heap
 * Input: `h_array` - The binary heap array
 *        `a_size` - The size of the array. A pointer to `size` located in main().
 *        `value` - The value that is to be inserted in the array
 * Returns: Void function. Performs all operations on inputted array.
 */

    *a_size += 1;

    int * a_copy = h_array;

    h_array = realloc(h_array, *a_size * sizeof(int));
    memcpy(h_array, a_copy, (*a_size - 2) * sizeof(int));

    h_array[*a_size - 1] = value;

    heapSort(h_array, *a_size, *min_max_ptr);
}

void heapSub(int h_array[], int *a_size, int *min_max_ptr) {
/* Summary: Subtracts the root value from the binary heap
 * Input: `h_array` - The binary heap array
 *        `a_size` - The size of the array. A pointer to `size` located in main().
 * Returns: Void function. Performs all operations on inputted array.
 */
    h_array[0] = h_array[*a_size - 1];

    int * a_copy = h_array;

    h_array = realloc(h_array, *a_size - 1 * sizeof(int));

    memcpy(h_array, a_copy, (*a_size - 1) * sizeof(int));

    *a_size -= 1; // Put here in order to not do any stupid calculations in the calls.

    heapSort(h_array, *a_size, *min_max_ptr);
}

int main(void) {
    char * user_input;
    int user_value;
    int debug = 0;

    // min_max = 0 to produce a max-heap, min_max = 1 to produce a min-heap
    int min_max = 0;
    int *min_max_ptr = &min_max;

    int size = 0;
    int *size_ptr = &size;

    // Binary Heap array, initialized here
    int * main_array = malloc(size * sizeof(int));

    // Start building binary heap with the following loop.
    while (strcmp(user_input, "q") != 0) {

        printf("Current Heap:\n");
        printArray(main_array, size);

        // Debug
        if (debug) {
            printf("Current Heap Size: %i\n", size);
        }

        printf("What is your input?: ");
        scanf("%s", user_input);

        // Debug
        if (debug) {
            printf("Current user input is: %s\n", user_input);
        }

        if (strcmp(user_input, "add") == 0) {

            printf("What # will you be adding to the heap?: ");
            scanf("%i", &user_value);
            heapAdd(main_array, size_ptr, user_value, min_max_ptr);

        } else if (strcmp(user_input, "sub") == 0) {

            printf("Subtracting %i from array\n", main_array[0]);
            heapSub(main_array, size_ptr, min_max_ptr);

        } else if (strcmp(user_input, "debug") == 0) {

            printf("Do you want to toggle debug mode(y/n): ");
            scanf("%s", user_input);

            if (strcmp(user_input, "y") == 0) {

                debug = (debug == 0) ? 1 : 0;
                printf("Debug is: %i", debug);

            } else {

                continue;
            }
        } else {

            printf("Incorrect Input, please read the instructions more\n\n");
        }

        printf("\n");
    }

    free(main_array);
    return 0;
}

So that's the code, and here are the test cases:

  1. Subtracting highest value from heap @ length = 2 test case 1
  2. Subtracting highest values from heap starting @ length = 4 and going to length = 2 test case 2

After that it seems like every other test case works fine (past length = 4 I can add and subtract from binary heap just fine and the sorting process works great). Thank you for your help :)

Kellan Martin
  • 26
  • 1
  • 5
  • 1) `while (strcmp(user_input, "q") != 0) {` This uses uninitialized variable `user_input`. `scanf("%s", user_input);` is the same. – BLUEPIXY Aug 15 '17 at 03:28
  • 1
    You reallocate in a function but that pointer is lost since it's not returned anywhere and after that you might keep using an old unallocated memory area. This most likely causes your issues. Use a debugger to step through your code. – Sami Kuhmonen Aug 15 '17 at 03:30
  • You might also want to run your code under `valgrind` (easy, but slow and potentially inaccurate, especially with optimizations) or AddressSanitizer (requires rebuilding, but very fast and accurate except under extreme conditions, and works even with optimizations) – o11c Aug 15 '17 at 03:46
  • Repeated reallocation for a change of one in the array size is not particularly good. You'd likely do better with allocating as much space as you'll need all at once, or keep track of what's allocated and what's in use and only allocate more when necessary. Jon Bentley's [Programming Pearls, 2nd Edn](https://www.amazon.com/dp/0201657880/) from 1999 and [More Programming Pearls](https://www.amazon.com/dp/0201118890/) from 1988 include code for handling array-based heaps as you are using. You can find an outline of that code in [this answer](https://stackoverflow.com/a/21742125) of mine. – Jonathan Leffler Aug 15 '17 at 05:35
  • Hey all I want to thank you a bunch for the help! It is much appreciated. I was able to get rid of those problems by going along with your suggestions; taking reallocating in functions away and instead creating a much larger allocation in `main()`. This has proved to be much more stable and the program works like a charm now. I would upvote if I could :/ – Kellan Martin Aug 15 '17 at 17:22

1 Answers1

0

I was able to reach a solution to my problems by making the following changes to my code:

void heapAdd(int h_array[], int *a_size, int value, int *min_max_ptr) {
/* Summary: Adds a value to the binary heap
 * Input: `h_array` - The binary heap array
 *        `a_size` - The size of the array. A pointer to `size` located in main().
 *        `value` - The value that is to be inserted in the array
 * Returns: Void function. Performs all operations on inputted array.
 */

    *a_size += 1;
    h_array[*a_size - 1] = value;
    heapSort(h_array, *a_size, *min_max_ptr);
}

void heapSub(int h_array[], int *a_size, int *min_max_ptr) {
/* Summary: Subtracts the root value from the binary heap
 * Input: `h_array` - The binary heap array
 *        `a_size` - The size of the array. A pointer to `size` located in main().
 * Returns: Void function. Performs all operations on inputted array.
 */
    h_array[0] = h_array[*a_size - 1];
    h_array[*a_size - 1] = 0;
    *a_size -= 1; // Put here in order to not do any stupid calculations in the calls.
    heapSort(h_array, *a_size, *min_max_ptr);
}

int main(void) {
    char * user_input;
    int user_value;
    int debug = 0;

    // min_max = 0 to produce a max-heap, min_max = 1 to produce a min-heap
    int min_max = 0;
    int *min_max_ptr = &min_max;

    int size = 0;
    int *size_ptr = &size;

    int alloc_size = 1000;
    int * main_array = malloc(alloc_size * sizeof(int));

    do {
        if (alloc_size - size < 2) {
            printf("Reallocating the main_array size");
            alloc_size += 1000;
            main_array = realloc(main_array, alloc_size * sizeof(int));
            if (main_array == NULL) {
                printf("realloc addition failed, exiting");
                exit(1);
            }
        } else if (alloc_size - size > 1002) {
            alloc_size -= 1000;
            main_array = realloc(main_array, alloc_size * sizeof(int));
            if (main_array == NULL) {
                printf("Realloc subtraction failed, exiting");
                exit(1);
            }
        }
        printf("Current Heap:\n");
        printArray(main_array, size);
        // Debug
        if (debug) {
            printf("Current Heap Size: %i\n", size);
        }
        printf("What is your input?: ");
        scanf("%s", user_input);
        // Debug
        if (debug) {
            printf("Current user input is: %s\n", user_input);
        }
        if (strcmp(user_input, "add") == 0) {
            printf("What # will you be adding to the heap?: ");
            scanf("%i", &user_value);
            heapAdd(main_array, size_ptr, user_value, min_max_ptr);
        } else if (strcmp(user_input, "sub") == 0) {
            if (size == 0) {
                printf("Can't subtract any more from the heap.\n");
                continue;
            } else {
                printf("Subtracting %i from array\n", main_array[0]);
                heapSub(main_array, size_ptr, min_max_ptr);
            }
        } else if (strcmp(user_input, "debug") == 0) {
            printf("Do you want to toggle debug mode(y/n): ");
            scanf("%s", user_input);
            if (strcmp(user_input, "y") == 0) {
                debug = (debug == 0) ? 1 : 0;
                printf("Debug is: %i", debug);
            } else {
                continue;
            }
        } else {
            printf("Incorrect Input, please read the instructions more fully\n\n");
        }
        printf("\n");
    } while (strcmp(user_input, "q") != 0);
    free(main_array);
    return 0;
}
Kellan Martin
  • 26
  • 1
  • 5