[英]How to allocate an array of structs in c?
I am writing a function that adds items to the shopping list.我正在编写一个将项目添加到购物清单的函数。 I understand how to limit the size of the shopping list, but what I want to do is that I want to use dynamic memory allocation by using
malloc ().
我了解如何限制购物清单的大小,但我想做的是我想通过使用
malloc ().
I want to get rid of the size and store the data as it comes in. Thus if 15 fields of data come in, I want to store 15 and not a specific size.我想摆脱大小并在数据进入时存储数据。因此,如果有 15 个数据字段进入,我想存储 15 个而不是特定的大小。 I'm assuming that I don't know how much data is coming into my program.
我假设我不知道有多少数据进入我的程序。
I am still a beginner, so let me know if I presented the question in a wrong way.我还是个初学者,所以如果我以错误的方式提出问题,请告诉我。
Thank you in advance, I appreciate your opinions.提前谢谢你,我很欣赏你的意见。
#define _CRT_SECURE_NO_WARNINGS
#include"ShoppingList.h"
#include<stdio.h>
#include<stdlib.h> // For malloc() and free()
void addItem(struct ShoppingList* list)
{
if (list->length > 4)
{
return 0;
}
printf("Name for product: ");
scanf("%s", list->itemList[list->length].productName);
do
{
printf("Enter the amount: ");
scanf("%f", &list->itemList[list->length].amount);
if (list->itemList[list->length].amount <= 0.0)
{
printf("Input is invalid.\n");
}
} while (list->itemList[list->length].amount <= 0.0);
printf("Enter unit of item: ");
scanf("%s", list->itemList[list->length].unit);
printf("%s was added to the shoppinglist.", list->itemList[list->length].productName);
list->length++;
}
#ifndef SHOPPING_LIST_H
#define SHOPPING_LIST_H
// Struct definitions
struct GroceryItem
{
char productName[20];
float amount;
char unit[10];
};
struct ShoppingList
{
int length;
struct GroceryItem itemList[5];
};
// Function declarations
void addItem(struct ShoppingList *list);
void printList(struct ShoppingList *list);
void editItem(struct ShoppingList *list);
void removeItem(struct ShoppingList *list);
void saveList(struct ShoppingList *list);
void loadList(struct ShoppingList* list);
#endif
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include "ShoppingList.h"
int main(void)
{
struct ShoppingList shoppingList;
shoppingList.length = 0; // The shopping list is empty at the start
int option;
do
{
printf("\n\nWelcome to the shopping list manager!\n");
printf("=====================================\n\n");
printf("1. Add an item\n");
printf("2. Display the shopping list\n");
printf("3. Remove an item\n");
printf("4. Change an item\n");
printf("5. Save list\n");
printf("6. Load list\n");
printf("7. Exit\n");
printf("What do you want to do? ");
scanf("%d", &option);
switch (option)
{
case 1: addItem(&shoppingList); break;
case 2: printList(&shoppingList); break;
case 3: removeItem(&shoppingList); break;
case 4: editItem(&shoppingList); break;
case 5: saveList(&shoppingList); break;
case 6: loadList(&shoppingList); break;
case 7: break;
default:
printf("Please enter a number between 1 and 7");
}
} while (option != 7);
return 0;
}
I would use flexible array members.我会使用灵活的数组成员。
typedef struct GroceryItem
{
char productName[20];
float amount;
char unit[10];
}GroceryItem;
typedef struct ShoppingList
{
size_t length;
GroceryItem itemList[];
}ShoppingList;
ShoppingList *additem(ShoppingList *sh, const GroceryItem *item)
{
size_t newsize = sh ? sh -> length + 1 : 1;
sh = realloc(sh, newsize * sizeof(sh -> itemList[0]) + sizeof(*sh));
if(sh)
{
sh -> length = newsize;
sh -> itemList[newsize - 1] = *item;
}
return sh;
}
But personally, I would rather use a linked list instead of an array for this task.但就个人而言,我宁愿使用链表而不是数组来完成这项任务。
There are several ways to achieve your target:有几种方法可以实现您的目标:
malloc(3)
use, and it is used only when you know in advance (in advance to allocate, but at run time, this is, not when the program is started, but for this usage of the array)malloc(3)
使用,并且仅在您事先知道时使用(提前分配,但在运行时,这是,不是在程序启动时,而是对于数组的这种用法) struct the_thing {
/* .... */
};
...
struct the_thing *array_of_things = malloc(number_of_things * sizeof(array_of_things[0]);
This will return a block of memory of size of the product of one element ( array_of_things[0]
is not a real expression this time, but the argument to the operator sizeof
) that calculates the size of the type of the evaluation of the used expression, and doesn't evaluates it ---this is important because you cannot evaluate that expression yet, as the pointer doesn't point yet to the allocated memory--- by the number of elements you are going to use) This converts your pointer (and the pointer arithmetic induced by using the [index]
notation) into an usable array (indeed it is a pointer to an array of elements. Important to say that the array is uninitialized and the data contents can be rubish and you have to initialize the elements, one by one, to the desired values. In the case you want an initialized array, you can use, instead, calloc(3)
that was defined specifically to allocate arrays of things.这将返回一个元素乘积大小的内存块(
array_of_things[0]
这次不是真正的表达式,而是运算符sizeof
的参数)计算所用表达式的评估类型的大小,并且不评估它---这很重要,因为你还不能评估那个表达式,因为指针还没有指向分配的内存---你要使用的元素数量)这会转换你的指针(以及使用[index]
表示法引起的指针算术)转换为可用数组(实际上它是指向元素数组的指针。重要的是要说数组未初始化并且数据内容可能很乱,你必须将元素一个一个地初始化为所需的值。如果你想要一个初始化的数组,你可以使用专门定义的calloc(3)
来分配事物的数组。
struct the_thing *array_of_things = calloc(number_of_things, sizeof(array_of_things[0]));
look at one detail, we have used a comma this time to specify two quantities as parameters to calloc()
, instead of passing the product of both.看一个细节,这次我们使用逗号将两个量指定为
calloc()
的参数,而不是传递两者的乘积。 The result is that calloc(3)
returns an initialized array (everything is initialized to zeros) and also, you specify separately the number of elements and the size of one element.结果是
calloc(3)
返回一个已初始化的数组(所有内容都初始化为零),并且您分别指定元素的数量和一个元素的大小。 This call is specific (but not mandatory specific) to create dynamic arrays.此调用特定于(但不是强制特定的)创建动态数组。 Both calls take as arguments the size of the total block of memory to allocate, and the number of elements and the element size, respectively.
这两个调用分别将要分配的内存块的总大小、元素数量和元素大小作为参数。
realloc()
that does the dirty work for your.realloc()
可以为你做脏活。 struct the_thing *array_of_things = NULL;
/* now I need n1 elements */
array_of_things = realloc(array_of_things, n1 * sizeof(array_of_things[0]));
with this, array_of_things
has passed to hold no element (because it was assigned NULL
) to allocate n1
elements.有了这个,
array_of_things
已经传递给没有元素(因为它被分配了NULL
)来分配n1
元素。 The normal workings allow you to work with this array, until you get to it's limit, now things get more interesting:正常工作允许你使用这个数组,直到你达到它的极限,现在事情变得更有趣了:
/* now I'll need n2 elements (n2 can be larger or smaller than n1 before) */
array_of_things = realloc(array_of_things, n2 * sizeof(array_of_things[0]));
this call (with an already valid pointer) will try to see if the allocation can be solved in place, but if it cannot, a new array will be allocated with enough space to hold n2
elements, the n1
previous elements will be copied into the second array, and the new allocated pointer will be returned, and the previous one will be freed.此调用(使用已经有效的指针)将尝试查看分配是否可以就地解决,但如果不能,将分配一个新数组,该数组具有足够的空间来容纳
n2
个元素,之前的n1
个元素将被复制到第二个数组,新分配的指针将被返回,前一个将被释放。
This has a possibly complicated drawback, is that you cannot have pointers pointing to element (or element fields) inside the array, because if the new array is repositioned in memory this will make all those pointers invalid (they will still be pointing to the old places, where now nothing exists) But if you can handle that, everything is fine.这可能有一个复杂的缺点,就是你不能让指针指向数组内的元素(或元素字段),因为如果新数组在内存中重新定位,这将使所有这些指针无效(它们仍将指向旧数组地方,现在什么都不存在)但是如果你能处理好,一切都很好。
I wrote a macro function to handle this situation and it works fine (except when I have pointers pointing inside the array that I have to rearrange all those pointers when I use the macro) The macro requires an array A
, with two associated variables, A_len
and A_cap
(both of type size_t
) to be declared with the array:我写了一个宏函数来处理这种情况并且它工作正常(除非我有指向数组内部的指针,当我使用宏时我必须重新排列所有这些指针)宏需要一个数组
A
,有两个相关变量, A_len
和A_cap
(均为size_t
类型)用数组声明:
#define DYNARRAY_GROW(_array _elem_type, _need, _increment) do { \
if (_array##_len + (_need) > _array##_cap) { \
_array##_cap += (_increment); \
_array = (_elem_type *) realloc( \
_array, _array##_cap * sizeof _array[0]); \
} \
} while (0)
that you declare your array as:您将数组声明为:
struct the_thing *array_of_things;
size_t array_of_things_len = 0;
size_t array_of_things_cap = 0;
and then, you use it:然后,你使用它:
/* I need n1 elements */
DYNARRAY_GROW(array_of_things, /* the array */
struct the_thing, /* the type of element (see note below) */
1, /* only one element more needed */
10); /* in case of grow, 10 elements are to be added */
In which the parameters其中参数
array_of_things
is the array name. array_of_things
是数组名称。 See from the macro definition that the _cap
suffixed variable and the _len
suffixed variables are derived from the name used here, so names are tied together (this makes less error prone the code)_cap
后缀变量和_len
后缀变量都是从这里使用的名字派生而来的,所以名字是绑在一起的(这样代码不容易出错)struct the_thing
is the type of an array element. struct the_thing
是数组元素的类型。 You will see that it is used only to cast the returned pointer from realloc()
to the proper type (thing that normally is discouraged in C, but for environments in which C code is tested using C++ testing code ---like GTest/GMock--- this is required, to avoid the resulting error from the C++ compiler) So while this cast is not only unnecessary in C (and even dangerous to use) it is forbidden in C++, and to allow compatibility with C++ (and being part of a tested macro) has been provided for convenience.realloc()
转换为正确的类型(这在 C 中通常是不鼓励的,但对于使用 C++ 测试代码测试 C 代码的环境——如 GTest/GMock --- 这是必需的,以避免 C++ 编译器产生的错误)因此,虽然这种转换不仅在 C 中是不必要的(甚至使用起来很危险),但在 C++ 中是被禁止的,并且允许与 C++ 兼容(并且是一部分为方便起见,提供了经过测试的宏)。1
is the amount of required elements. 1
是所需元素的数量。 It is supposed that you call this macro to add some number of elements (normally one) to it, one shot, this is the amount of such elements you are going to add.10
is the amount of elements to grow in case a grow is required. 10
是在需要增长的情况下要增长的元素数量。 Despite the number of elements to grow you need, it is often far more efficient to allocate a bigger ammount so not every time we need one element we allocate only one, but a bunch of them, so the number of total calls to realloc()
is decreased, adding efficiency to the overal code.realloc()
的总调用次数减少了,增加了整体代码的效率。 In your case, for example:在你的情况下,例如:
struct ShoppingList *
shopping_list = NULL;
size_t shopping_list_cap = 0,
shopping_list_len = 0;
void addItem(
struct ShoppingList *element)
{
DYNARRAY_GROW(shopping_list,
struct ShoppingList,
1, /* what is needed */
10); /* what we get in that case. */
/* after that we're able to add it (we have one
* free slot at least to add an element) */
shopping_list[shopping_list_len++] = *element; /* copy the data */
}
Of course, when you are done with the shopping list, you need to set all three global variables to NULL
, 0
and 0
.当然,完成购物清单后,您需要将所有三个全局变量设置为
NULL
、 0
和0
。
free(shopping_list);
shopping_list = NULL;
shopping_list_len = shopping_list_cap = 0;
Note: I have deliberately not done error checking, because is not clear in your homework how it is to be handled the occasional (and rare) case that you get NULL
from realloc()
.注意:我故意没有进行错误检查,因为在你的家庭作业中不清楚如何处理你从
realloc()
得到NULL
的偶然(和罕见)情况。 This case is a bit cumbersome because it requires you to make a second copy of the pointer before calling realloc()
(as the value stored in shopping_list
will be destroyed by the assignment of NULL
to the pointer variable, in case the realloc()
fails)这种情况有点麻烦,因为它要求您在调用
realloc()
之前制作指针的第二个副本(因为存储在shopping_list
中的值将通过将NULL
分配给指针变量来销毁,以防realloc()
失败)
Normally, a memory allocation fail means you have some memory leak somewhere, as:通常,内存分配失败意味着你在某处有一些内存泄漏,如:
NULL
return value, then continue (if you still call) without the allocation (eg try a lower increment, wait some time so we have more memory, etc.) by recovering the copy into the array pointer.NULL
返回值,则通过将副本恢复到数组指针中来继续(如果您仍然调用)而不进行分配(例如尝试较低的增量,等待一段时间以便我们有更多内存等)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.