引发未处理的异常:写访问冲突

时间:2020-07-03 13:24:54

标签: c mergesort

我正在分析具有 100 000 个元素的Merge Sort算法,它在具有 10000 个元素的情况下运行良好。它显示了我的异常。这是我的代码:

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();
}

引发未处理的异常:写访问冲突。 temp 为0x1110112。发生

2 个答案:

答案 0 :(得分:3)

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

错了。

索引范围从leftIndexrightIndex到两端都包括在内,因此将处理rightIndex - leftIndex + 1个元素,当元素数量的这种指定将导致缺少元素时leftIndex为零。

访问缓冲区的边界将调用未定义的行为,并且似乎碰巧使用更少的元素。

该行应为

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

您还应该释放数组:在函数free(temp);的末尾添加Merge

答案 1 :(得分:1)

错误不明显:为临时数组分配的大小不正确。在大多数情况下,分配的大小会太大,而不是calloc(rightIndex, sizeof(int));,而calloc(rightIndex - leftIndex + 1, sizeof(int));可能会带来问题,因为您永远不会释放这些临时数组,但是当leftIndex为0时,分配的块太短,将元素存储在temp[rightIndex]时会导致不确定的行为。

在许多情况下,未定义的行为可能不会引起注意:在数组末尾存储值可能会导致malloc竞技场损坏,但是为对齐目的,分配的空间末尾可能会有一些松弛腐败是无害的。对于较大的块(通常> 128KB),malloc可能会将内存映射为一个单独的段,从而导致此写尝试落在可访问的内存之外,从而导致崩溃。

还请注意,您的错误是易于出错的约定的副作用,该约定将rightIndex作为最后一个元素的索引传递,而不是一次通过切片末尾传递索引。编程课上广泛教授这种有害的习俗,在学生中引起很多混乱。它要求对代码进行+1 / -1的调整,这些调整很容易忘记。

这是经过修改的版本,具有更加惯用的C技术:

#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;
}