[英]How to allocate an array of structs in 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("\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;
}
我會使用靈活的數組成員。
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]
表示法引起的指針算術)轉換為可用數組(實際上它是指向元素數組的指針。重要的是要說數組未初始化並且數據內容可能很亂,你必須將元素一個一個地初始化為所需的值。如果你想要一個初始化的數組,你可以使用專門定義的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_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)
您將數組聲明為:
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
后綴變量都是從這里使用的名字派生而來的,所以名字是綁在一起的(這樣代碼不容易出錯)struct the_thing
是數組元素的類型。 您會看到它僅用於將返回的指針從realloc()
轉換為正確的類型(這在 C 中通常是不鼓勵的,但對於使用 C++ 測試代碼測試 C 代碼的環境——如 GTest/GMock --- 這是必需的,以避免 C++ 編譯器產生的錯誤)因此,雖然這種轉換不僅在 C 中是不必要的(甚至使用起來很危險),但在 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 */
}
當然,完成購物清單后,您需要將所有三個全局變量設置為NULL
、 0
和0
。
free(shopping_list);
shopping_list = NULL;
shopping_list_len = shopping_list_cap = 0;
注意:我故意沒有進行錯誤檢查,因為在你的家庭作業中不清楚如何處理你從realloc()
得到NULL
的偶然(和罕見)情況。 這種情況有點麻煩,因為它要求您在調用realloc()
之前制作指針的第二個副本(因為存儲在shopping_list
中的值將通過將NULL
分配給指針變量來銷毀,以防realloc()
失敗)
通常,內存分配失敗意味着你在某處有一些內存泄漏,如:
NULL
返回值,則通過將副本恢復到數組指針中來繼續(如果您仍然調用)而不進行分配(例如嘗試較低的增量,等待一段時間以便我們有更多內存等)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.