简体   繁体   English

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

[英]How do I properly allocate an array within a struct with malloc and realloc in C?

I'm trying to implement a stack in C, while also trying to learn C. My background is mostly in higher languages (like Python), so a lot of the memory allocation is new to me. 我正在尝试使用C实现堆栈,同时还尝试学习C。我的背景主要是使用高级语言(如Python),所以很多内存分配对我来说都是新的。

I have a program that works as expected, but throws warnings that make me believe I'm doing something wrong. 我有一个可以按预期工作的程序,但是会发出警告,使我相信自己做错了什么。

Here is the code: 这是代码:

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);
}

Here is the output: 这是输出:

Stack top: 3, length: 2.

Which is expected. 这是预期的。 But during compilation, I get the following errors: 但是在编译期间,出现以下错误:

> 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);

When you have an empty array declared at the end of the structure like you have, it's called a flexible array member . 当像您一样在结构末尾声明一个空数组时,它称为灵活数组成员 And you allocate it not by allocating just the array member, but by allocating the whole structure . 而且,您分配它的方式不只是分配数组成员,而是分配整个结构

Like eg 像例如

stack *x = malloc(sizeof *x + sizeof s->items[0] * 32);

The above malloc call allocates space for the structure itself (note the use of the dereference operator for sizeof *x ) plus space for an array of 32 elements. 上面的malloc调用为结构本身分配空间(请注意对sizeof *x使用解引用运算符),再为32个元素的数组分配空间。

It's either the above, or change the member to be a pointer. 要么是上面的,要么将成员更改为指针。

This is an array of unspecified size 这是未指定大小的数组

int items[];

This is a pointer 这是一个指针

int *items;

The latter is what you use with malloc / realloc to make use of dynamically allocated memory. 后者是与malloc / realloc一起使用的功能,以利用动态分配的内存。

Also, because you're doing (for example) 另外,因为您正在做(例如)

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

...you're de-referencing items so that it becomes an int which is why you're getting those particular warnings. ...您取消引用项目,使其成为一个int ,这就是为什么您收到这些特定警告的原因。

Your belief is correct. 你的信念是正确的。 Usually - that is almost always - warnings from C compiler are signs of grave programming errors that will cause serious problems. 通常-几乎总是-来自C编译器的警告是严重编程错误的信号,这将导致严重的问题。 Quoting Shooting yourself in the foot in various programming languages : 报价用各种编程语言射击自己

C C

  • You shoot yourself in the foot. 你用脚开枪射击自己。
  • You shoot yourself in the foot and then nobody else can figure out what you did. 你用脚开枪射击自己,然后没人能弄清楚你做了什么。

The problem is that you're coding as if items was a pointer to int, yet you have declared and defined it as a flexible array member (FAM), which is an entirely different beast altogether. 问题是您正在编码,就好像items指向 int的指针一样 ,但是您已将其声明并定义为灵活数组成员 (FAM),这完全是另一回事。 And since assigning to an array would produce an error , ie 而且由于分配给数组将产生错误 ,即

x->items = malloc(0);

would be an error, you've come up with something that compiles with just warnings. 将会是一个错误,您提出了一些仅带有警告的内容。 Remember that errors are better than warnings, because they stop you from shooting yourself into foot. 请记住,错误比警告要好,因为它们会阻止您将自己击shooting。


The solution is to declare items as a pointer to int instead: 解决方案是将items声明为指向int指针

int *items;

and use 和使用

x->items = ...;

to get the pointer behaviour you expect. 获得您期望的指针行为。

Also, 也,

free(&(x->items[i]));

is very wrong, since you never allocated the i th integer to begin with; 这是非常错误的,因为您从未分配过第i整数。 they were objects in the array. 它们是数组中的对象。 Also, you don't need malloc(0) ; 另外,您不需要malloc(0) just initialize with a null pointer: 只需使用空指针初始化即可:

x->items = NULL;

realloc and free wouldn't mind the null pointer. reallocfree不会介意null指针。


The flexible array member means that the last element in the structure is an array of indefinite length, so in malloc you would reserve enough memory for it too: 灵活的数组成员意味着结构中的最后一个元素是不确定长度的数组,因此在malloc您还将为其保留足够的内存:

stack *x = malloc(sizeof x + sizeof *x->items * n_items);

The flexible array member is used in CPython for objects like str , bytes or tuple that are of immutable length - it is slightly faster to use a FAM instead of a pointer elsewhere, and it saves memory - especially with shorter strings or tuples. 可变数组成员在CPython中用于长度不变的strbytestuple类的对象-使用FAM代替其他地方的指针稍快一些,并且节省了内存-尤其是使用较短的字符串或元组时。


Finally, notice that your stack becomes slower the more it grows - the reason is because you're always allocating just one more element. 最后,请注意,堆栈越增长,堆栈就会变得越慢-原因是因为您总是只分配一个元素。 Instead, you should scale the size of the stack by a factor (1.3, 1.5, 2.0?), so that insertions run in O(1) time as opposed to O(n) ; 取而代之的是,您应该将堆栈的大小缩放一个因数 (1.3、1.5、2.0?),以便插入在O(1)时间而不是O(n) and consider what will happen should realloc fail - perhaps you should be more loud about it! 考虑重新realloc失败时将发生的情况-也许您应该对此大声一点!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM