[英]Struggling with malloc/realloc and structures
我真的很難理解在C中使用結構時使用malloc
和realloc
時實際發生的情況。我試圖解決電話簿問題,必須創建一個可以添加,刪除和顯示條目的電話簿。 條目數可以無限,因此我必須動態定義結構數組。 我的刪除功能還必須找到所需的條目,然后移回所有條目,然后使用realloc
釋放最后一個條目。 我認為這是對realloc
的基本理解將真正幫助我的地方。
我認為我可以正常工作的唯一部分是添加功能,但我仍然不知道我是否正確設置了它-我只知道它在運行時就可以正常工作。 如果某個基本水平的人可以幫助我,我將不勝感激。
這是我的一堆代碼:-(
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getEntry(int *);
typedef struct person {
char fname[20];
char lname[20];
int number[10];
}person;
void getInfo(int *,int,person*);
void delInfo(int*,int,person*);
void showInfo(int*,int,person*);
int main(){
int a=0;
int i=0;
int con=0;
person* contacts=(person*)malloc(sizeof(person));
person* pcontacts;
pcontacts=(person*)calloc(0,sizeof(person));
getEntry(&a);
while (a!=4){
switch (a){
case 1:
pcontacts=(person*)realloc(pcontacts,con* sizeof(person));
getInfo(&con,i,contacts);
break;
case 2:
delInfo(&con,i,contacts);
break;
case 3:
showInfo(&con,i,contacts);
break;
default:
printf("\n Error in response. Please try again: ");
break;
}
getEntry(&a);
}
printf("\n Thank you for using the Phone Book Application!\n\n");
free(contacts);
return 0;
}
void getEntry(int *a1){
int b;
printf("\n\n\n Phone Book Application\n\n 1) Add Friend\n 2) Delete Friend\n 3) Show Phone Book Entries\n 4) Exit\n\n Make a selection: ");
scanf("%d",&b);
*a1 = b;
}
void getInfo(int *con,int i,person*contacts){
printf("\n Enter first name: ");
scanf("%s",contacts[*con].fname);
printf(" Enter last name: ");
scanf("%s",contacts[*con].lname);
printf(" Enter telephone number without spaces or hyphens: ");
scanf("%d",contacts[*con].number);
(*con)++;
printf("\n Entry Saved.");
}
void delInfo(int *con,int i,person*contacts){
char delfirst[20];
char dellast[20];
printf("\n First Name: ");
scanf("%s",delfirst);
printf(" Last Name: ");
scanf("%s",dellast);
for (i=0; i<*con;i++){
if (delfirst==contacts[i].fname && dellast==contacts[i].lname){
}
}
}
void showInfo(int *con,int i,person*contacts){
char nullstr[1]={"\0"};
if (*con>0){
printf("\n Current Listings:\n");
for (i=0;i<*con;i++){
if (strcmp(nullstr,contacts[i].fname)!=0){
printf(" %s %s %i\n",contacts[i].fname,contacts[i].lname,contacts[i].number);
}
else {};
}
}
else {
printf("\n No Entries\n");
}
}
首先,讓我澄清另一個答案中提出的觀點:
關於可移植性的警告:根據《 GNU C庫參考手冊》,ISO C之前的實現可能不支持此行為。
除非您正在使用具有數十年歷史的嵌入式硬件及其專有的編譯器,否則,您很少 (如果有的話)會遇到至少不支持C89(ANSI C)標准的編譯器。
現在已經解決了,讓我們繼續進行malloc()
和realloc()
。 要了解后者,讓我們看一下前者。
malloc()
的手冊頁指出以下內容:
malloc()
。 。 。 [和]realloc()
。 。 。 函數分配內存。 對齊分配的內存,以便可以將其用於任何數據類型。 。 。free()
函數釋放通過前面的分配函數創建的分配。
這種幫助我們。 基本上,您的操作系統“擁有”計算機上實際安裝的所有可用內存。 系統在內核級別分配所有RAM, 交換空間等。
但是,用戶空間中的進程(例如您的程序)不希望/不需要管理系統中的所有內存-更不用說所涉及的安全風險了(請考慮一下,如果我願意在另一個進程中不修改內存就可以了嗎?安防措施!)。
因此,操作系統將管理進程的內存,並為進程使用分配系統內存的不同部分。
一般而言,操作系統將內存划分為所謂的塊或頁面 。 簡而言之,如果將內存划分為適當的大小(通常為2的指數,並且至少是CPU 寄存器大小的倍數),則會得到非常陡峭的優化,因為CPU只能以特定的粒度訪問內存。
由於所有這些,程序本身必須要求內核為其分配內存,以便程序可以使用它並確保它是唯一可以訪問它的進程。
malloc()
是執行此任務的標准函數調用。 當您以一定長度調用malloc()
時,系統將嘗試查找未使用的系統內存的連續范圍,並將其映射到進程的虛擬內存空間中,以存儲您請求的內存的地址和長度。
如果找不到足夠的內存,它只會返回NULL
(0)-您可以在手冊頁中閱讀:)
隨后,當您調用free()
,它將在虛擬內存表中查找地址,檢索您最初使用malloc()
的長度,並告訴系統該進程不再需要該內存區域。
最后一點: 分段錯誤通常是由不再分配的內存訪問引起的,即使曾經分配過。 這就是為什么發明了RAII之類的C ++范例的原因-只是為了減少過早釋放內存或根本不釋放內存的可能性(即,使用一堆直到進程返回才釋放的內存)。
有了以上知識, realloc()
應該是小菜一碟。 再次讓我們看一下其手冊頁:
realloc(ptr, size)
函數嘗試將ptr
指向的分配大小更改為size
,並返回ptr
。 如果沒有足夠的空間來擴大ptr
指向的內存分配,則realloc()
創建一個新分配,復制ptr
指向的舊數據以適應新分配的數量,釋放舊分配,然后返回指向已分配內存的指針。 如果ptr
為NULL
,則realloc()
等同於對size
字節的malloc()
調用。 如果size
為零且ptr
不為NULL
,則分配一個最小的新對象,並釋放原始對象。
這很長,但是基本上可以告訴您您需要知道的確切信息。
realloc()
與malloc()
非常相似,您為其傳遞了一個長度,但同時還傳遞了一個先前的malloc()
指向它的指針以調整大小。
簡而言之,您基本上是在擴展先前通過malloc()
分配的內存區域。
混亂通常是由於realloc()
並不總是擴展初始內存區域而引起的,而且,甚至會free()
您傳入其中的內存區域!
記住我上面關於連續內存的觀點:如果您要求malloc()
分配10個字節,而這10個字節恰好恰好恰好位於已經分配的其他兩個內存區域之間,然后您想要將10個字節的區域調整為,例如20個字節-會發生什么?
由於內存區域必須是連續的,這意味着必須找到一個新的內存空間才能連續容納所有20個字節!
在那種情況下, realloc()
足以找到具有足夠空間的新區域,從原始內存區域復制所有舊數據(由您提供的realloc()
指向的指針),釋放舊區域,然后返回新區域的地址。
這就是為什么必須始終存儲realloc()
的返回地址的原因:
char *ptr = malloc(100);
ptr = realloc(ptr, 400); // not guaranteed the first and
// second addresses will be the same!
處理此問題的一種方法是,聲明一個指向person
結構的指針,然后像數組一樣訪問引用的內存:
person *contacts;
您可以不使用任何條目進行初始化:
int num_entries = 0;
contacts = NULL;
然后,當您添加條目時:
num_entries++;
contacts = realloc(contacts, sizeof(*contacts) * num_entries);
然后,您可以將新的聯系信息讀入新的結構,即contacts[num_entries - 1]
。 刪除條目時,將person
結構復制到動態數組中的新位置后(如果條目的順序不是問題,則可以將最后一個條目復制到要刪除的條目上): contacts[delete_index] = contacts[num_entries - 1]
),您只需執行以下操作:
num_entries--;
contacts = realloc(contacts, sizeof(*contacts) * num_entries);
您可能應該檢查realloc()
的返回值以處理分配錯誤。
realloc()
一個不錯的功能是,當傳遞一個空指針時,它的行為就好像已經調用了malloc()
一樣。 因此,您可以從代表空聯系人列表的空指針開始,並對所有添加和刪除使用realloc()
。 關於可移植性的警告:根據《 GNU C庫參考手冊》 ,ISO C之前的實現可能不支持此行為。 我認為這表示您可以安全地將其與ANSI C及更高版本一起使用。 我從來沒有對此有任何疑問,但是也許有人會發出這樣的聲音。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.