我正在分析具有 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。发生
答案 0 :(得分:3)
行
int *temp = (int *) calloc(rightIndex, sizeof(int));
错了。
索引范围从leftIndex
和rightIndex
到两端都包括在内,因此将处理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;
}