Unhandled exception thrown: write access violation

Viewed 274

I'm analyzing Merge Sort algorithm with 100 000 elements, it was running fine with 10 000 elements. It showing me exception. Here is my code:

void Merge(int *array, int leftIndex, int middleIndex, int rightIndex) {
    int *temp = (int *)calloc(rightIndex, sizeof(int));
    int i = leftIndex, j = middleIndex + 1, k = 0;
    while (i <= middleIndex && j <= rightIndex) {
        if (array[i] < array[j]) {
            temp[k++] = array[i++]; // Here it is showing me exception
        } else {
            temp[k++] = array[j++];
        }
    }
    while (i <= middleIndex) {
        temp[k++] = array[i++];
    }
    while (j <= rightIndex) {
        temp[k++] = array[j++];
    }
    for (i = leftIndex, j = 0; i <= rightIndex; i++, j++) {
        array[i] = temp[j];
    }
}

void MergeSort(int *array, int leftIndex, int rightIndex) {
    if (leftIndex < rightIndex) {
        int middleIndex = (leftIndex + rightIndex) / 2;
        MergeSort(array, leftIndex, middleIndex);
        MergeSort(array, (middleIndex + 1), rightIndex);
        Merge(array, leftIndex, middleIndex, rightIndex);
    }
}

void main() {
    for (long size = 10; size <= 100000; size *= 10) {
        int *array = RandomArray(size);
        MergeSort(array, 0, size - 1);
    }
    _getch();
}

Unhandled exception thrown: write access violation. temp was 0x1110112. occurred

2 Answers

The line

    int *temp = (int *) calloc(rightIndex, sizeof(int));

is wrong.

The index range is from leftIndex and rightIndex with both ends are included, so rightIndex - leftIndex + 1 elements will be dealt with and this specification of number of elements will lead to lack of elements when leftIndex is zero.

Accessing out-of-bounds of buffers will invoke undefined behavior, and it seems it happened to work with fewer elements.

The line should be

    int *temp = (int *) calloc(rightIndex - leftIndex + 1, sizeof(int));

Also you should free the array: add free(temp); at the end of the function Merge.

The error is not obvious: the size allocated for the temporary array is incorrect. Instead of calloc(rightIndex, sizeof(int)); you should have calloc(rightIndex - leftIndex + 1, sizeof(int)); In most cases the allocated size will be much too large, which may pose a problem as you never free these temporary arrays, but when leftIndex is 0, the allocated block is too short, causing undefined behavior when you store the element at temp[rightIndex].

Undefined behavior may go unnoticed for many cases: storing a value beyond the end of the array may cause a corruption of the malloc arena, but the space allocated may have some slack at the end for alignment purposes so this corruption is harmless. For larger blocks (usually > 128KB) malloc may map memory as a separate segment, causing this write attempt to fall outside of accessible memory, causing a crash as you experience.

Note also that your mistake is a side effect of the error prone convention of passing rightIndex as the index of the last element instead of passing the index once past the end of the slice. This harmful convention is widely taught in programming classes, causing a lot of confusion among students. It requires +1/-1 adjustments in the code which are easy to forget.

Here is a modified version with a more idiomatic C cnvention:

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

int *RandomArray(long size);

void Merge(int *array, int leftIndex, int middleIndex, int rightIndex) {
    int *temp = calloc(rightIndex - leftIndex, sizeof(int));
    int i = leftIndex, j = middleIndex, k = 0;
    while (i < middleIndex && j < rightIndex) {
        if (array[i] <= array[j]) {
            temp[k++] = array[i++];
        } else {
            temp[k++] = array[j++];
        }
    }
    while (i < middleIndex) {
        temp[k++] = array[i++];
    }
    /* no need to copy the remaining elements from array[j] */
    for (i = leftIndex, k = 0; i < j; i++) {
        array[i] = temp[k++];
    }
    free(temp);
}

void MergeSort(int *array, int leftIndex, int rightIndex) {
    if (rightIndex - leftIndex > 1) {
        int middleIndex = leftIndex + (rightIndex - leftIndex) / 2;
        MergeSort(array, leftIndex, middleIndex);
        MergeSort(array, middleIndex, rightIndex);
        Merge(array, leftIndex, middleIndex, rightIndex);
    }
}

int main() {
    for (long size = 10; size <= 100000; size *= 10) {
        int *array = RandomArray(size);
        MergeSort(array, 0, size);
        free(array);
    }
    _getch();
    return 0;
}
Related