尝试动态分配二维数组时出现段错误
Segfault when trying to dynamicaly realocate a 2 dimensional array
我正在开发一个系统,该系统具有多个相互交互的结构 area
,这些区域存储在一个名为 storage
的常规数组中,它会在您需要时动态重新分配添加或删除它们。
我处理交互的方式是使用一个名为 overlap
的二维数组,它存储 1 个字节的值。存储的每个元素都有一列和一行。
overlap[x][y]
处的值表示元素 storage[x]
与元素 storage[y]
.
的交互
这些区域还有 layers
和 layerMasks
来控制它们可以与哪些元素交互。
例如,第 1 层中具有掩码 2、3 和 4 的区域。可能仅与第 2、3 和 4 层中的区域交互。并且只能与具有掩码 1 的区域交互。层的范围从 0 到63.
为此,我需要将区域放置在存储空间内并以能够区分图层的方式重叠,为此,我将使用数组 sPos
表示存储位置。这个数组将有 65 个元素,每层一个元素加一个。 sPos 中的值是存储中第一个区域在层中等于或大于 sPos 值的索引的位置,sPos[64]
是存储的大小。
这就是我处理事情的方式:
area * addArea(area * toAdd) {
// realocating the storage and overlap.
storage = realloc(storage, sizeof(area *) * (sPos[64] + 1));
if (!storage) {error handling} // Error handling is a printf("addArea\n") and a return NULL.
overlap = realloc(overlap, sizeof(unsigned char *) * (sPos[64] + 1));
if (!overlap) {error handling}
// Realloc works as malloc for NULL pointers, so setting this to NULL will allocate it when reallocating the rows.
overlap[sPos[64]] = NULL;
// Moving the elements in layers greater than or equal to toAdd->layer.
for (int i = sPos[64]; i > sPos[toAdd->layer]; i--) overlap[i + 1] = overlap[i];
// reallocating the rows of the overlap, and moving their elements as well.
for (int i = 0; i < sPos[64]; i++) {
overlap[i] = realloc(overlap[i], sizeof(unsigned char) * sPos[64] + 1);
if (!overlap[i]) {error handling}
for (int j = sPos[64]; j > sPos[toAdd->layer]; j--) overlap[i][j + 1] = overlap[i][j];
}
// Seting the new elements of overlap to 0 (no interaction).
for (int i = 0; i <= sPos[64]; i++) {
overlap[sPos[toAdd->layer]][i] = 0;
overlap[i][sPos[toAdd->layer]] = 0;
}
// Moving the elements in storage to place toAdd in the position sPos[toAdd->layer]
for (int i = sPos[64]; i > sPos[toAdd->layer]; i--) storage[i] = storage[i - 1];
storage[sPos[toAdd->layer]] = toAdd;
// Adding 1 to every element of sPos with an index greater than toAdd->layer.
for (int i = toAdd->layer + 1; i <= 64; i++) sPos[i]++;
return toAdd; // returns the argument, or NULL in case of error.
}
添加不同图层的区域时,似乎没有什么不好的事情发生。但是当我尝试在同一层中添加区域时,我得到了一个没有错误警告的段错误。通常当有 4 个或更多元素并试图在占用层中添加一个时。
使用gdb,我发现错误发生在重新分配重叠的行时,但我不太明白为什么。
首先,为了提高代码的可读性和可调试性,不要试图在 for 循环声明的同一行内联语句。
也就是说,不要这样做:
for (int i = 0; i < N; i++) doSomething(i);
这样做:
for (int i = 0; i < N; i++) {
doSomething(i);
}
当在调试器中逐行单步调试时,上面的代码更容易使用。
回到手头的原始问题。您的内存损坏问题。
你分配了这个:
overlap = realloc(overlap, sizeof(Unsigned char *) * (sPos[64] + 1));
让我们假设 sPos[64]
等于 10
。因此,您分配了 10+1 == 11
字节。
因此,overlap
的有效数组索引值来自 [0..10]
(含)。
然后按如下方式初始化数组:
for (int i = sPos[64]; i > sPos[toAdd->layer]; i--) {
overlap[i + 1] = overlap[i];
}
for 循环中执行的第一条语句将是:
overlap[11] = overlap[10];
糟糕! overlap[11]
超出范围。因此,当您写入该内存位置时,未定义的行为。您可能损坏了堆。
你可能想要这样的东西(我正在假设你真正想做什么)
int lastIndex = sPos[64];
int firstIndex = toAdd->layer + 1;
for (int i = lastIndex; i >= firstIndex; i--) {
overlap[i] = overlap[i-1];
}
此外,您可以使用 memmove 为您完成这项工作,前提是您正确计算指针数学。 (同样,我正在对您的数组边界做出假设):
memmove(overlap+firstIndex+1, overlap+firstInex, lastIndex-firstIndex);
我还要指出,当您尝试将此数组右移时,绝对不能保证 overlap[i-1]
不指向垃圾。如果您的 realloc
大小小于或等于该数组的原始分配长度,则没有问题。但是,如果它正在“增长”数组,您应该假设 realloc 返回了一个全新的数组,并且原始 overlap
数组已被丢弃。
我的总体建议是,当您在循环中使用它们时,您要了解已分配的每个数组的有效数组索引是什么。这很可能不是您唯一的“因 1 个错误而关闭”。
我正在开发一个系统,该系统具有多个相互交互的结构 area
,这些区域存储在一个名为 storage
的常规数组中,它会在您需要时动态重新分配添加或删除它们。
我处理交互的方式是使用一个名为 overlap
的二维数组,它存储 1 个字节的值。存储的每个元素都有一列和一行。
overlap[x][y]
处的值表示元素 storage[x]
与元素 storage[y]
.
这些区域还有 layers
和 layerMasks
来控制它们可以与哪些元素交互。
例如,第 1 层中具有掩码 2、3 和 4 的区域。可能仅与第 2、3 和 4 层中的区域交互。并且只能与具有掩码 1 的区域交互。层的范围从 0 到63.
为此,我需要将区域放置在存储空间内并以能够区分图层的方式重叠,为此,我将使用数组 sPos
表示存储位置。这个数组将有 65 个元素,每层一个元素加一个。 sPos 中的值是存储中第一个区域在层中等于或大于 sPos 值的索引的位置,sPos[64]
是存储的大小。
这就是我处理事情的方式:
area * addArea(area * toAdd) {
// realocating the storage and overlap.
storage = realloc(storage, sizeof(area *) * (sPos[64] + 1));
if (!storage) {error handling} // Error handling is a printf("addArea\n") and a return NULL.
overlap = realloc(overlap, sizeof(unsigned char *) * (sPos[64] + 1));
if (!overlap) {error handling}
// Realloc works as malloc for NULL pointers, so setting this to NULL will allocate it when reallocating the rows.
overlap[sPos[64]] = NULL;
// Moving the elements in layers greater than or equal to toAdd->layer.
for (int i = sPos[64]; i > sPos[toAdd->layer]; i--) overlap[i + 1] = overlap[i];
// reallocating the rows of the overlap, and moving their elements as well.
for (int i = 0; i < sPos[64]; i++) {
overlap[i] = realloc(overlap[i], sizeof(unsigned char) * sPos[64] + 1);
if (!overlap[i]) {error handling}
for (int j = sPos[64]; j > sPos[toAdd->layer]; j--) overlap[i][j + 1] = overlap[i][j];
}
// Seting the new elements of overlap to 0 (no interaction).
for (int i = 0; i <= sPos[64]; i++) {
overlap[sPos[toAdd->layer]][i] = 0;
overlap[i][sPos[toAdd->layer]] = 0;
}
// Moving the elements in storage to place toAdd in the position sPos[toAdd->layer]
for (int i = sPos[64]; i > sPos[toAdd->layer]; i--) storage[i] = storage[i - 1];
storage[sPos[toAdd->layer]] = toAdd;
// Adding 1 to every element of sPos with an index greater than toAdd->layer.
for (int i = toAdd->layer + 1; i <= 64; i++) sPos[i]++;
return toAdd; // returns the argument, or NULL in case of error.
}
添加不同图层的区域时,似乎没有什么不好的事情发生。但是当我尝试在同一层中添加区域时,我得到了一个没有错误警告的段错误。通常当有 4 个或更多元素并试图在占用层中添加一个时。
使用gdb,我发现错误发生在重新分配重叠的行时,但我不太明白为什么。
首先,为了提高代码的可读性和可调试性,不要试图在 for 循环声明的同一行内联语句。
也就是说,不要这样做:
for (int i = 0; i < N; i++) doSomething(i);
这样做:
for (int i = 0; i < N; i++) {
doSomething(i);
}
当在调试器中逐行单步调试时,上面的代码更容易使用。
回到手头的原始问题。您的内存损坏问题。
你分配了这个:
overlap = realloc(overlap, sizeof(Unsigned char *) * (sPos[64] + 1));
让我们假设 sPos[64]
等于 10
。因此,您分配了 10+1 == 11
字节。
因此,overlap
的有效数组索引值来自 [0..10]
(含)。
然后按如下方式初始化数组:
for (int i = sPos[64]; i > sPos[toAdd->layer]; i--) {
overlap[i + 1] = overlap[i];
}
for 循环中执行的第一条语句将是:
overlap[11] = overlap[10];
糟糕! overlap[11]
超出范围。因此,当您写入该内存位置时,未定义的行为。您可能损坏了堆。
你可能想要这样的东西(我正在假设你真正想做什么)
int lastIndex = sPos[64];
int firstIndex = toAdd->layer + 1;
for (int i = lastIndex; i >= firstIndex; i--) {
overlap[i] = overlap[i-1];
}
此外,您可以使用 memmove 为您完成这项工作,前提是您正确计算指针数学。 (同样,我正在对您的数组边界做出假设):
memmove(overlap+firstIndex+1, overlap+firstInex, lastIndex-firstIndex);
我还要指出,当您尝试将此数组右移时,绝对不能保证 overlap[i-1]
不指向垃圾。如果您的 realloc
大小小于或等于该数组的原始分配长度,则没有问题。但是,如果它正在“增长”数组,您应该假设 realloc 返回了一个全新的数组,并且原始 overlap
数组已被丢弃。
我的总体建议是,当您在循环中使用它们时,您要了解已分配的每个数组的有效数组索引是什么。这很可能不是您唯一的“因 1 个错误而关闭”。