如何在C中使用malloc和realloc在结构中正确分配数组?

如何在C中使用malloc和realloc在结构中正确分配数组?,c,arrays,pointers,memory,struct,C,Arrays,Pointers,Memory,Struct,我正在尝试用C实现一个堆栈,同时也在尝试学习C。我的背景主要是Python等高级语言,所以很多内存分配对我来说都是新的 我有一个程序可以按预期工作,但抛出的警告让我相信我做错了什么 代码如下: typedef struct { int num_items; int top; int items[]; } stack; void push(stack *st, int n) { st->num_items++; int* tmp = realloc

我正在尝试用C实现一个堆栈,同时也在尝试学习C。我的背景主要是Python等高级语言,所以很多内存分配对我来说都是新的

我有一个程序可以按预期工作,但抛出的警告让我相信我做错了什么

代码如下:

typedef struct {
    int num_items;
    int top;
    int items[];
} stack;

void push(stack *st, int n) {
    st->num_items++;

    int* tmp = realloc(st->items, (st->num_items) * sizeof(int));

    if (tmp) {
        *(st->items) = tmp;
    }

    st->items[st->num_items - 1] = n;
    st->top = n;
}

int main() {
    stack *x = malloc(sizeof(x));
    x->num_items = 0;
    x->top = 0;
    *(x->items) = malloc(0);

    push(x, 2);
    push(x, 3);

    printf("Stack top: %d, length: %d.\n", x->top, x->num_items);

    for (int i = 0; i < x->num_items; i++) {
        free(&(x->items[i]));
    }
    free(x->items);
    free(x);
}
这是意料之中的。但在编译过程中,我发现以下错误:

> gcc -x c -o driver driver.c
driver.c: In function 'push':
driver.c:16:16: warning: assignment makes integer from pointer without a cast
    *(st->items) = tmp;
...
driver.c: In function 'main':
driver.c:27:14: warning: assignment makes integer from pointer without a cast
    *(x->items) = malloc(0);

这是一个未指定大小的数组

int items[];
这是一个指针

int *items;
后者是您使用malloc/realloc来利用动态分配的内存

还有,因为你正在做的,例如

*(x->items) = malloc(0);

…您正在取消引用项,使其成为int,这就是为什么您会收到这些特定警告。

这是一个未指定大小的数组

int items[];
这是一个指针

int *items;
后者是您使用malloc/realloc来利用动态分配的内存

还有,因为你正在做的,例如

*(x->items) = malloc(0);

…您正在取消引用项,使其成为int,这就是您收到这些特定警告的原因。

您的想法是正确的。通常-这几乎总是-来自C编译器的警告是严重编程错误的标志,这些错误将导致严重的问题。引述:

C

你射中了自己的脚。 你射中了自己的脚,然后没人知道你做了什么。 问题是,您的编码方式好像items是指向int的指针,但您已经将其声明并定义为一个灵活的数组成员FAM,这是一个完全不同的beast。由于分配给数组会产生错误,即

这将是一个错误,你已经想出了一些编译只是警告。请记住,错误比警告好,因为它们会阻止你自食其果

解决方案是将项声明为指向int的指针:

和使用

x->items = ...;
获取您期望的指针行为

而且

这是非常错误的,因为您从未分配第i个整数作为开始;它们是数组中的对象。而且,您不需要malloc0;只需使用空指针初始化:

x->items = NULL;
realloc和free不会介意空指针

灵活的数组成员意味着结构中的最后一个元素是一个长度不定的数组,因此在malloc中,您也可以为它保留足够的内存:

stack *x = malloc(sizeof x + sizeof *x->items * n_items);
灵活的数组成员在CPython中用于长度不可变的str、bytes或tuple等对象—在其他地方使用FAM而不是指针稍微快一点,并且节省内存—特别是对于较短的字符串或tuple


最后,请注意,堆栈的增长速度越慢,原因是您总是只分配一个以上的元素。相反,您应该按因子1.3、1.5、2.0?缩放堆栈的大小,以便插入以O1时间运行,而不是以On时间运行;考虑一下RealCLC失败了会发生什么,也许你应该对它大声些。p> 你的信念是正确的。通常-这几乎总是-来自C编译器的警告是严重编程错误的标志,这些错误将导致严重的问题。引述:

C

你射中了自己的脚。 你射中了自己的脚,然后没人知道你做了什么。 问题是,您的编码方式好像items是指向int的指针,但您已经将其声明并定义为一个灵活的数组成员FAM,这是一个完全不同的beast。由于分配给数组会产生错误,即

这将是一个错误,你已经想出了一些编译只是警告。请记住,错误比警告好,因为它们会阻止你自食其果

解决方案是将项声明为指向int的指针:

和使用

x->items = ...;
获取您期望的指针行为

而且

这是非常错误的,因为您从未分配第i个整数作为开始;它们是数组中的对象。而且,您不需要malloc0;只需使用空指针初始化:

x->items = NULL;
realloc和free不会介意空指针

灵活的数组成员意味着结构中的最后一个元素是一个长度不定的数组,因此在malloc中,您也可以为它保留足够的内存:

stack *x = malloc(sizeof x + sizeof *x->items * n_items);
灵活的数组成员在CPython中用于长度不可变的str、bytes或tuple等对象—在其他地方使用FAM而不是指针稍微快一点,并且节省内存—特别是对于较短的字符串或tuple


最后,请注意,堆栈的增长速度越慢,原因是您总是只分配一个以上的元素。相反,您应该按因子1.3、1.5、2.0?缩放堆栈的大小,以便插入以O1时间运行,而不是以On时间运行;考虑一下RealCLC失败了会发生什么,也许你应该对它大声些。p> 当您在 这个结构的末端,就像你所看到的,叫做a。分配它不是通过只分配数组成员,而是通过分配整个结构

比如

stack *x = malloc(sizeof *x + sizeof s->items[0] * 32);
上面的malloc调用为结构本身分配空间请注意,sizeof*x使用了解引用操作符,加上32个元素数组的空间


可以是上面的,也可以将成员更改为指针。

如果在结构末尾声明了一个空数组,则称为空数组。分配它不是通过只分配数组成员,而是通过分配整个结构

比如

stack *x = malloc(sizeof *x + sizeof s->items[0] * 32);
上面的malloc调用为结构本身分配空间请注意,sizeof*x使用了解引用操作符,加上32个元素数组的空间


要么如上所述,要么将成员更改为指针。

更改成员将是合乎逻辑的方法,否则需要使代码句柄realloc更改整个对象的内存位置structure@ChrisTurner符合事实的但只要记住正确地更新例如x,就可以很容易地解决这个问题。@Someprogrammerdude@ChrisTurner,如果我在main和push中使用它,就可以像stack*tmp=reallocst,sizeof*st+st->num_items*sizeofst->items[0];然后将st指定为tmp,这是首选解决方案吗?还是更愿意只更改成员?@Dylan正如Chris Turner指出的那样,使用指针成员可能更容易,但这里确实没有正确的解决方案。都是有效的,,而且这取决于你自己的喜好,你想使用哪一个。@Dylan你必须重新定义push以获取堆栈**,并将tmp分配给*st-否则一旦你离开函数,你就会失去更改更改成员将是合乎逻辑的方法,否则你需要使代码句柄realloc更改函数的内存位置整体structure@ChrisTurner是的,但只要记住正确地更新例如x,就可以很容易地解决这个问题。@Someprogrammerdude@ChrisTurner,如果我在main和push中使用它,则会执行类似stack*tmp=reallocst,sizeof*st+st->num_items*sizeofst->items[0];然后将st指定为tmp,这是首选解决方案吗?还是更愿意只更改成员?@Dylan正如Chris Turner指出的那样,使用指针成员可能更容易,但这里确实没有正确的解决方案。都是有效的,,您想使用哪一个取决于您自己的喜好。@Dylan您必须重新定义push以获取堆栈**,并将tmp分配给*st-否则,一旦您离开函数,您将丢失更改选择此作为可接受的解决方案,因为它在一个位置包含最多的信息,甚至提供了改进堆栈的帮助算法。谢谢之所以选择此作为可接受的解决方案,是因为它在一个位置包含最多的信息,甚至可以帮助改进算法。谢谢