尝试动态分配二维数组时出现段错误

Segfault when trying to dynamicaly realocate a 2 dimensional array

我正在开发一个系统,该系统具有多个相互交互的结构 area,这些区域存储在一个名为 storage 的常规数组中,它会在您需要时动态重新分配添加或删除它们。 我处理交互的方式是使用一个名为 overlap 的二维数组,它存储 1 个字节的值。存储的每个元素都有一列和一行。 overlap[x][y] 处的值表示元素 storage[x] 与元素 storage[y].

的交互

这些区域还有 layerslayerMasks 来控制它们可以与哪些元素交互。 例如,第 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 个错误而关闭”。