簡體   English   中英

與malloc / realloc和結構作斗爭

[英]Struggling with malloc/realloc and structures

我真的很難理解在C中使用結構時使用mallocrealloc時實際發生的情況。我試圖解決電話簿問題,必須創建一個可以添加,刪除和顯示條目的電話簿。 條目數可以無限,因此我必須動態定義結構數組。 我的刪除功能還必須找到所需的條目,然后移回所有條目,然后使用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指向的舊數據以適應新分配的數量,釋放舊分配,然后返回指向已分配內存的指針。 如果ptrNULL ,則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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM