【问题标题】:Understanding Malloc and Realloc in regards to an array of structs了解有关结构数组的 Malloc 和 Realloc
【发布时间】:2018-11-30 18:23:12
【问题描述】:

我已经为 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);
    }

}

我已经尝试了我发现的所有变体,但我宁愿不要盲目地输入 & 和 * 直到发生某些事情。

【问题讨论】:

  • 你能给出结构坐标和结构三角形的定义吗?
  • 当然,我将它们添加到我的代码示例中。对此感到抱歉
  • 尝试将 tangles 的地址发送到 add_traingles 函数。即addTrangles(&tangles, t);
  • addTriangle(triangles, t); 您的编译器应该抱怨第一个参数的间接级别不同。你期待一个struct triangle **,但你传递了一个struct triangle *。您应该始终在编译器中启用警告。使用 -Wall -Wextra
  • 与您的问题无关,但在为坐标分配内存时会造成内存泄漏。你可以简单地使用一个变量。不需要指针。

标签: c arrays struct malloc realloc


【解决方案1】:

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

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

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

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

    允许将空指针传递给free,这符合您的预期(它什么也不做),但是既然您知道trianglesthen 分支中是NULL,只需调用exit.

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

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

【讨论】:

  • @GoswinvonBrederlow 是的,我会改变它。
  • 您应该在答案中包含修改后的来源,并说明您更改它的原因,并且应该可以接受。
  • 另一个可能的改进:不需要malloc struct coordinate 对象(目前已泄露)。只需struct coordinate a = { 1*i, 2*i }; t-&gt;point[0] = a; 等。
  • @aschepler 这是一个很好的建议,但与 OP 所面临的问题并不严格相关,所以我很难包含它。
  • 这似乎确实有效。如果我可能会问,当您在 main 中给出 &triangles 然后在 addTriangle 中期望 **triangles 时,地址会发生什么?您正在使用三角形指针的指针给出三角形指针的地址并重新分配?这似乎是多余的,但这就是我首先在这里的原因
【解决方案2】:

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

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

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

我已经注意到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);
    }
}

【讨论】:

    【解决方案3】:

    把函数调用改成

    addTriangle(&triangles, t);
    

    【讨论】:

      猜你喜欢
      • 2012-09-19
      • 2021-01-15
      • 1970-01-01
      • 1970-01-01
      • 2018-08-29
      • 1970-01-01
      • 2017-02-22
      • 2021-03-04
      • 2018-07-13
      相关资源
      最近更新 更多