c - C 中的动态数组 - realloc

标签 c

我知道如何构建动态分配的数组,但不知道如何增长它们。

例如我有以下界面..

void insertVertex( vertex p1, vertex out[], int *size);

此方法获取一个顶点并将其存储到 out 数组中。存储顶点后,我增加了长度以供将来调用。

p1 - 是我要添加的顶点。

out[] - 是我需要存储它的数组(它总是满的)

length - 当前长度

顶点定义为..

typedef struct Vertex{
int x;
int y;
} Vertex;

这就是我在 Java 中使用的..

Vertex tempOut = new Vertex[size +1];
//Code to deep copy each object over
tempOut[size] = p1;
out = tempOut;

这就是我相信我可以在 c.. 中使用的东西

out = realloc(out, (*size + 1) * sizeof(Vertex));
out[(*size)] = p1;

但是,我不断收到一条错误消息,指出对象不是动态分配的。

我找到了解决此问题的解决方案。我没有使用 Vertex*,而是改用 Vertex** 并存储指针与顶点。然而,在切换所有内容后,我发现我忽略了一个事实,即单元测试将为我提供一个 Vertex out[],所有内容都必须存储在其中。

我也尝试过以下方法,但没有成功。

Vertex* temp = (Vertex *)malloc((*size + 1) * sizeof(Vertex));
for(int i = 0; i < (*size); i++)
{
temp[i] = out[i];
}
out = temp;

但是,无论我在这两个之后进行测试时做什么,返回的数组都没有改变。

更新 - 要求的信息

out - 被定义为顶点数组(Vertex out[])

它最初是用我的多边形中的顶点数构建的。例如。

out = (Vertex *)malloc(vertexInPolygon * sizeof(Vertex))

其中 vertexInPolygon 是多边形中顶点数的整数。

length 是一个错字,应该是 size。

Size是一个整型指针

int *size = 0;

每当一个顶点在裁剪平面中时,我们将其添加到顶点数组中并将大小增加一。

更新

为了更好地解释我自己,我想出了一个简短的程序来展示我正在尝试做的事情。

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

typedef struct Vertex {
    int x, y;
} Vertex;

void addPointerToArray(Vertex v1, Vertex out[], int *size);

void addPointerToArray(Vertex v1, Vertex out[], int *size)
{
    int newSize = *size;
    newSize++;
    
    out = realloc(out, newSize * sizeof(Vertex));
    out[(*size)] = v1;
    
    //  Update Size
    *size = newSize;
}

int main (int argc, const char * argv[])
{
    //  This would normally be provided by the polygon
    int *size = malloc(sizeof(int)); *size = 3;
    
    //  Build and add initial vertex
    Vertex *out = (Vertex *)malloc((*size) * sizeof(Vertex));
    Vertex v1; v1.x = 1; v1.y =1;
    Vertex v2; v2.x = 2; v2.y =2;
    Vertex v3; v3.x = 3; v3.y =3;
    
    out[0] = v1;
    out[1] = v2;
    out[2] = v3;
    
    //  Add vertex
    //  This should add the vertex to the last position of out
    //  Should also increase the size by 1;
    Vertex vertexToAdd; vertexToAdd.x = 9; vertexToAdd.y = 9;
    addPointerToArray(vertexToAdd, out, size);
    
    for(int i =0; i < (*size); i++)
    {
        printf("Vertx: (%i, %i) Location: %i\n", out[i].x, out[i].y, i);
    }
   
}

最佳答案

一个长期存在的问题是您没有从 addPointerToArray() 函数返回更新后的数组指针:

void addPointerToArray(Vertex v1, Vertex out[], int *size)
{
    int newSize = *size;
    newSize++;

    out = realloc(out, newSize * sizeof(Vertex));
    out[(*size)] = v1;

    //  Update Size
    *size = newSize;
}

当您重新分配空间时,它可以移动到一个新位置,因此 realloc() 的返回值不必与输入指针相同。当您添加到数组时没有其他内存分配正在进行时这可能会起作用,因为 realloc() 将在有空间的情况下扩展现有分配,但一旦您开始它就会失败在读取顶点时分配其他数据。有几种方法可以解决此问题:

Vertex *addPointerToArray(Vertex v1, Vertex out[], int *size)
{
    int newSize = *size;
    newSize++;

    out = realloc(out, newSize * sizeof(Vertex));
    out[(*size)] = v1;

    //  Update Size
    *size = newSize;
    return out;
}

和调用:

out = addPointerToArray(vertexToAdd, out, size);

或者,您可以传入一个指向数组的指针:

void addPointerToArray(Vertex v1, Vertex **out, int *size)
{
    int newSize = *size;
    newSize++;

    *out = realloc(*out, newSize * sizeof(Vertex));
    (*out)[(*size)] = v1;

    //  Update Size
    *size = newSize;
}

和调用:

out = addPointerToArray(vertexToAdd, &out, size);

这些重写都没有解决微妙的内存泄漏问题。问题是,如果您用返回值覆盖传递给 realloc() 的值,但 realloc() 失败,您将丢失指向(仍然)分配的数组的指针- 内存泄漏。当您使用 realloc() 时,请使用像这样的成语:

Vertex *new_space = realloc(out, newSize * sizeof(Vertex));
if (new_space != 0)
    out = new_space;
else
    ...deal with error...but out has not been destroyed!...

请注意,使用 realloc() 一次添加一个新项目会导致(可能导致)二次行为。你最好分配一大块内存——例如,将分配的空间加倍:

int newSize = *size * 2;

如果担心过度分配,在读取循环结束时,可以使用realloc() 将分配的空间缩小到数组的确切大小。但是,还有更多的簿记工作要做;你需要值:分配给数组的顶点数,以及实际使用的顶点数。

最后,至少现在,请注意您真的应该严格保持一致并使用 addPointerToArray() 将前三个条目添加到数组中。我可能会使用类似于此(未经测试的)代码的东西:

struct VertexList
{
    size_t    num_alloc;
    size_t    num_inuse;
    Vertex   *list;
};

void initVertexList(VertexList *array)
{
    // C99: *array = (VertexList){ 0, 0, 0 };
    // Verbose C99: *array = (VertexList){ .num_inuse = 0, .num_alloc = 0, .list = 0 };
    array->num_inuse = 0;
    array->num_alloc = 0;
    array->list      = 0;
}

void addPointerToArray(Vertex v1, VertexList *array)
{
    if (array->num_inuse >= array->num_alloc)
    {
        assert(array->num_inuse == array->num_alloc);
        size_t new_size = (array->num_alloc + 2) * 2;
        Vertex *new_list = realloc(array->list, new_size * sizeof(Vertex));
        if (new_list == 0)
            ...deal with out of memory condition...
        array->num_alloc = new_size;
        array->list      = new_list;
    }
    array->list[array->num_inuse++] = v1;
}

这使用了 realloc() 的反直觉属性,如果传入的指针为空,它将执行 malloc()。您可以改为检查 array->list == 0,然后使用 malloc(),否则使用 realloc()

您可能会注意到这种结构也简化了调用代码;您不再需要处理主程序中单独的 int *size;(及其内存分配);大小被有效地捆绑到 VertexList 结构中作为 num_inuse。主程序现在可以开始了:

int main(void)
{
    VertexList array;
    initVertexList(&array);
    addPointerToArray((Vertex){ 1, 1 }, &array);  // C99 compound literal
    addPointerToArray((Vertex){ 2, 2 }, &array);
    addPointerToArray((Vertex){ 3, 3 }, &array);
    addPointerToArray((Vertex){ 9, 9 }, &array);

    for (int i = 0; i < array->num_inuse; i++)
        printf("Vertex %d: (%d, %d)\n", i, array->list[i].x, array->list[i].y, i);

    return 0;
}

(巧合的是这个序列只会调用一次内存分配,因为新的大小(old_size + 2) * 2第一次给数组分配了4个元素。很容易练习通过添加新点或将公式改进为 (old_size + 1) * 2 或 ...

来重新分配

如果您计划从内存分配失败中恢复(而不是在它发生时退出),那么您应该修改 addPointerToArray() 以返回一个状态(成功,不成功)。

此外,函数名称可能应该是 addPointToArray()addVertexToArray() 甚至 addVertexToList()

关于c - C 中的动态数组 - realloc,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7563308/

相关文章:

c - 提高C程序的计算速度

C typedef 和指向结构的指针

c - 我一直遇到段错误,我不确定为什么

c - do...while 循环有问题

c - KdTree C 实现导致核心转储

c - 这个链表没有变化,在main函数中不起作用

c - 有没有更简单的方法来创建具有给定结构的链表?

c - 函数返回后esp的值是多少?

c - 运行时调度抽象

c - 如何使用C中的函数记录用户输入数字的频率