如何在c中分配结构数组



我正在编写一个函数,将商品添加到购物列表中。我知道如何限制购物清单的大小,但我想做的是,我想通过使用malloc ().来使用动态内存分配

我想去掉大小,并在数据进入时存储数据。因此,如果有15个字段的数据进入,我想存储15个,而不是特定的大小。我假设我不知道有多少数据进入我的程序。

我还是一个初学者,所以如果我提出问题的方式不对,请告诉我。

提前谢谢你,我很感激你的意见。

#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("nnWelcome to the shopping list manager!n");
printf("=====================================nn");
printf("1. Add an itemn");
printf("2. Display the shopping listn");
printf("3. Remove an itemn");
printf("4. Change an itemn");
printf("5. Save listn");
printf("6. Load listn");
printf("7. Exitn");
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;
}

我会使用灵活的数组成员。

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

但就我个人而言,我更愿意使用链表而不是数组来执行此任务。

有几种方法可以实现您的目标:

  • 第一个不涉及任何机器,但涉及malloc(3)的使用,并且只有当您提前知道时才使用它(提前分配,但在运行时,这不是在程序启动时,而是在阵列的这种使用中)
struct the_thing {
/* .... */
};
...
struct the_thing *array_of_things = malloc(number_of_things * sizeof(array_of_things[0]);

这将返回一个元素乘积大小的内存块(array_of_things[0]这次不是真正的表达式,而是运算符sizeof的参数),该内存块计算所用表达式的求值类型的大小,但不对其求值——这很重要,因为您还不能对该表达式求值,因为指针还没有指向分配的内存——根据您将要使用的元素数量)这将指针(以及使用[index]表示法得出的指针算术)转换为可用的数组(事实上,它是一个指向元素数组的指针。重要的是,数组未初始化,数据内容可能是rubish,您必须将元素一个接一个地初始化为所需值。如果您想要一个初始化的数组,您可以使用专为分配数组而定义的calloc(3)

struct the_thing *array_of_things = calloc(number_of_things, sizeof(array_of_things[0]));

看一个细节,我们这次使用逗号指定两个量作为calloc()的参数,而不是传递两者的乘积。结果是calloc(3)返回一个初始化的数组(所有数组都初始化为零),并且您可以单独指定元素的数量和一个元素的大小。此调用是特定的(但不是强制性的),用于创建动态数组。这两个调用都将要分配的内存总块的大小、元素数量和元素大小分别作为参数。

  • 当您需要越来越多的元素时,需要使数组动态增长时,就会出现第二种方法。这可以用前面的函数来解决,但有一个函数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]));

这样一来,array_of_things就不再持有任何元素(因为它被分配了NULL)来分配n1元素。正常的工作方式允许你使用这个数组,直到你达到它的极限,现在事情变得更有趣了:

/* 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]));

这个调用(使用一个已经有效的指针)将尝试查看是否可以在适当的位置解决分配,但如果不能,则将为新数组分配足够的空间来容纳n2元素,n1之前的元素将复制到第二个数组中,并且将返回新分配的指针,并释放之前的指针。

这可能有一个复杂的缺点,就是不能让指针指向数组中的元素(或元素字段),因为如果新数组在内存中重新定位,这将使所有这些指针无效(它们仍然指向旧的位置,现在什么都不存在)。但如果你能处理好这一点,一切都会好起来。

我写了一个宏函数来处理这种情况,它工作得很好(除了当我的指针指向数组内部时,我必须在使用宏时重新排列所有指针)。宏需要一个数组A,以及两个相关的变量A_lenA_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)

您将数组声明为:

struct the_thing *array_of_things;
size_t array_of_things_len = 0;
size_t array_of_things_cap = 0;

然后,你使用它:

/* 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 */

其中参数

  • array_of_things是数组名称。从宏定义中可以看出,_cap后缀变量和_len后缀变量是从这里使用的名称派生的,因此名称被绑定在一起(这使得代码不太容易出错)
  • CCD_ 22是数组元素的类型。您将看到,它只用于将返回的指针从realloc()强制转换为正确的类型(这在C中通常是不鼓励的,但对于使用C++测试代码测试C代码的环境(如GTest/GLock),这是必需的,以避免C++编译器产生错误),并且为了方便而提供了允许与C++的兼容性(并且是被测试宏的一部分)
  • 1是所需元素的数量。假设你调用这个宏来添加一些元素(通常是一个),一次,这就是你要添加的元素数量
  • 10是在需要生长的情况下要生长的元素的量。尽管你需要增加元素的数量,但分配更大的弹药数量通常会更高效,所以不是每次我们需要一个元素时,我们只分配一个,而是分配一堆,所以对realloc()的总调用次数减少了,从而提高了整体代码的效率

在您的情况下,例如:

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 */
}

当然,当您完成购物列表时,您需要将所有三个全局变量都设置为NULL00

free(shopping_list);
shopping_list = NULL;
shopping_list_len = shopping_list_cap = 0;

注意:我故意不做错误检查,因为在你的家庭作业中不清楚如何处理偶尔(和罕见)从realloc()得到NULL的情况。这种情况有点麻烦,因为它要求您在调用realloc()之前制作指针的第二个副本(因为存储在shopping_list中的值将被NULL分配给指针变量所破坏,以防realloc()失败)

通常,内存分配失败意味着你在某个地方有内存泄漏,例如:

  • 系统现在有足够的虚拟内存可分配给每个进程(这意味着物理内存加上交换空间),因此不太可能在单个进程中分配机器的内存总量
  • 如果你需要限制进程的数据内存量,假设你知道如何处理内存分配错误,为这种情况编写一个有效的解决方案,只需复制指针,运行上面的代码,如果你得到NULL返回值,然后在没有分配的情况下继续(如果您仍然调用)(例如,尝试较低的增量,等待一段时间,这样我们就有更多的内存,等等),将副本恢复到数组指针中
  • 如果您在内存较小的嵌入式系统中运行,那么很多时候都不希望动态分配内存,因为几乎没有堆空间来进行动态内存分配。然后,您必须预先分配静态数组并以这种方式运行

相关内容

  • 没有找到相关文章

最新更新