了解关于结构数组的 Malloc 和 Realloc

Understanding Malloc and Realloc in regards to an array of structs

我已经为 malloc 和 realloc 背后的想法苦苦挣扎了一段时间,目前我在动态创建结构数组方面遇到了问题。我有一个 struct triangle,它本身由一个 struct coordinates 数组组成。我希望能够拥有一个尽可能大的 triangles 数组,但是每次我尝试增加数组的长度时,似乎什么都没有发生。 Realloc 不会失败,malloc 也不会。但是新的三角形没有插入到我的数组中。这是我的代码供参考。

#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
struct coordinate {
    int x;
    int y;
};

struct triangle {
    struct coordinate point[3];
};
  static size_t size = 0;

static void addTriangle(struct triangle **triangles, struct triangle *t) {
    struct triangle *ts = (struct triangle*) realloc(*triangles, (size+1) * sizeof(struct triangle));
    if(ts == NULL) {
        free(ts);
        exit(EXIT_FAILURE);
    }

    *triangles = ts;
    triangles[size] = t;
    size++;

}

int main() {
    struct triangle* triangles = (struct triangle *) malloc(sizeof(struct triangle));
    if(triangles == NULL) {
        free(triangles);
        exit(EXIT_FAILURE);
    }
    for(int i = 0; i < 2; i++) {
        struct coordinate *a = malloc(sizeof(struct coordinate));
        a->x = 1 * i;
        a->y = 2 * i;
        struct coordinate *b = malloc(sizeof(struct coordinate));
        b->x = 3 * i;
        b->y = 4 * i;
        struct coordinate *c = malloc(sizeof(struct coordinate));
        c->x = 5 * i;
        c->y = 6 * i;
        struct triangle *t = malloc(sizeof(struct triangle));
        t->point[0] = *a;
        t->point[1] = *b;
        t->point[2] = *c;

        addTriangle(triangles, t);
    }

}

我已经尝试了我发现的每一种变体,但我宁愿不要盲目地加入 & 和 * 直到有什么事情发生。

按原样,您的程序在将未初始化的 *triangles 传递给 realloc 时会调用未定义的行为:https://taas.trust-in-soft.com/tsnippet/t/9ff94de4。您可能打算在 main.

中调用它时传递 &triangles

main 中的调用更改为 addTriangle(&triangles, t);,下一个问题是 addTriangle: https://taas.trust-in-soft.com/tsnippet/t/658228a1 中的越界访问。同样,这可能是因为您的间接级别错误,意思是 (*triangles)[size] 而不是 triangles[size].

如果我将 triangles[size] = t; 行更改为 (*triangles)[size] = *t; 则不会出现未定义的行为。你应该检查这个程序是否仍然做你想要的,因为它被修改了:https://taas.trust-in-soft.com/tsnippet/t/8915bd2d

最终版本:

#include <string.h>
#include <stdlib.h>
struct coordinate {
    int x;
    int y;
};

struct triangle {
    struct coordinate point[3];
};
  static size_t size = 0;

static void addTriangle(struct triangle **triangles, struct triangle *t) {
    struct triangle *ts = (struct triangle*) realloc(*triangles, (size+1) * sizeof(struct triangle));
    if(ts == NULL) {
        free(ts);
        exit(EXIT_FAILURE);
    }

    *triangles = ts;
    (*triangles)[size] = *t; // a struct assignment
    size++;

}

int main() {
    struct triangle* triangles = (struct triangle *) malloc(sizeof(struct triangle));
    if(triangles == NULL) {
        free(triangles);
        exit(EXIT_FAILURE);
    }
    for(int i = 0; i < 2; i++) {
        struct coordinate *a = malloc(sizeof(struct coordinate));
        a->x = 1 * i;
        a->y = 2 * i;
        struct coordinate *b = malloc(sizeof(struct coordinate));
        b->x = 3 * i;
        b->y = 4 * i;
        struct coordinate *c = malloc(sizeof(struct coordinate));
        c->x = 5 * i;
        c->y = 6 * i;
        struct triangle *t = malloc(sizeof(struct triangle));
        t->point[0] = *a;
        t->point[1] = *b;
        t->point[2] = *c;

        addTriangle(&triangles, t); /* pass the address of triangles
           so that addTriangle can modify this variable's contents */
    }

}

旁注与您询问的问题没有直接关系

  1. 只要你用C编程,请do not cast the result ofmalloc。只需写 struct triangle* triangles = malloc(....

  2. 正如@aschepler 在评论中指出的那样,该程序仍然泄漏从 main 分配的内存块。这些可以在每次迭代结束时释放,而不添加任何未定义的行为:https://taas.trust-in-soft.com/tsnippet/t/a0705262。这样做,您可能会意识到 t->point[0] = *a;, ... 实际上是结构赋值,并且没有必要首先分配一个单独的 struct coordinate:您可以只填写每个 struct coordinate struct triangle 的成员。此外,也没有必要在 main 中分配 struct triangle:你可以为此使用局部变量,因为无论如何结构的内容将被函数 [=18= 复制]到main的局部变量triangles指向的数组。

  3. 如果 trianglesmain 中的空指针,您也不需要调用 free(triangles):

    struct triangle* triangles = (struct triangle *) malloc(...
    if(triangles == NULL) {
        free(triangles);
        exit(EXIT_FAILURE);
    }
    

    允许将空指针传递给 free,这会如您所愿(它什么也不做),但是因为您知道 trianglesNULL then 分支,只需调用 exit.

  4. 正在处理 realloc on the other hand is a subtle subject 的失败。您的程序做错了,但这并不重要,因为它会立即调用 exit

  5. 将有关 main 的局部变量 triangles 指向的已分配数组的信息存储在静态文件范围变量 size 中是不一致的。两者关系密切,应该属于同一范畴。由于需要addTriangle才能改变size,所以不能简单地将size移动为main的局部变量,而是可以将[=34]的局部变量移动=] 的 main 到文件范围,在 size 旁边。毕竟,如果您更愿意将 size 设为 main 的局部变量,则需要将其地址传递给函数 addTriangle,以便后者可以更新前者。

您可以将整个 for 循环替换为

struct triangle t = {{{i, 2*i},{3*i,4*i},{5*i,6*i}}};
addTriangle(&triangles, &t);

请注意参数前的&,因为你要传递两者的地址

我已经在评论中注意到 triangles[size] = t; 应该是 (*triangles)[size] = *t;

#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
struct coordinate {
    int x;
    int y;
};

struct triangle {
    struct coordinate point[3];
};
  static size_t size = 0;

static void addTriangle(struct triangle **triangles, struct triangle *t) {
    struct triangle *ts = realloc(*triangles, (size+1) * sizeof(struct triangle));
    if(ts == NULL) {
        exit(EXIT_FAILURE);
    }

    *triangles = ts;
    (*triangles)[size] = *t;
    size++;

}

int main() {
    struct triangle* triangles = malloc(sizeof(struct triangle));
    if(triangles == NULL) {
        exit(EXIT_FAILURE);
    }
    for(int i = 0; i < 2; i++) {
        struct triangle t = {{{i, 2*i},{3*i,4*i},{5*i,6*i}}};
        addTriangle(&triangles, &t);
    }
    for(int i = 0; i < size; i++) {
        printf("%d %d, %d %d, %d %d\n", triangles[i].point[0].x,
                                        triangles[i].point[0].y,
                                        triangles[i].point[1].x,
                                        triangles[i].point[1].y,
                                        triangles[i].point[2].x,
                                        triangles[i].point[2].y);
    }
}

将函数调用改为

addTriangle(&triangles, t);