新聞中心

        EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 結(jié)構(gòu)體中動態(tài)內(nèi)存的管理(malloc和free)

        結(jié)構(gòu)體中動態(tài)內(nèi)存的管理(malloc和free)

        作者: 時(shí)間:2016-12-01 來源:網(wǎng)絡(luò) 收藏
        C語言中內(nèi)存的管理主要是依據(jù)malloc和free實(shí)現(xiàn)的,其中malloc主要是實(shí)現(xiàn)內(nèi)存的分配,而free則是實(shí)現(xiàn)內(nèi)存的釋放。雖然這是我們已經(jīng)很熟悉的,但是還是存在一些問題。特別是當(dāng)結(jié)構(gòu)體中存在指針的情況下,各種問題也就會展現(xiàn)出來。
        其中最大的問題是:結(jié)構(gòu)體中指針變量沒有指向一塊合法的內(nèi)存空間,就對指針參數(shù)進(jìn)行操作,這也是很多C語言程序員經(jīng)常犯的錯(cuò)誤。
        簡單的實(shí)例如下:
        struct student
        {
        char *name;
        int score;
        }stu,*pstu;
        int main()
        {
        strcpy(stu.name,"Jimy");
        stu.score = 99;
        strcpy(pstu->name,"Jimy");
        pstu->score = 99;
        }
        這種代碼是新手經(jīng)常犯的錯(cuò)誤,其中的主要錯(cuò)誤是指針變量沒有指向一塊內(nèi)存空間,其中包括ptest沒有指向一塊內(nèi)存空間,同時(shí)結(jié)構(gòu)體中的指針變量name也沒有指向一塊內(nèi)存空間。
        這種代碼一般都會編譯通過,但是運(yùn)行過程中會發(fā)生新手編程經(jīng)常出現(xiàn)的段錯(cuò)誤Segmentation fault (core dumped),我通過gdb對程序進(jìn)行調(diào)試發(fā)現(xiàn)了存在的一些問題。其中stu.name中的內(nèi)容是0x0,也就是地址0x0。這樣我就知道了0x0為什么會發(fā)生段錯(cuò)誤了,因?yàn)樵贚inux中進(jìn)程都有一個(gè)獨(dú)立的虛擬存儲空間4G,但是其中在最底部的0x0是沒有映射的,具體的參看進(jìn)程的存儲器映射關(guān)系。0x0并沒有映射,這樣發(fā)生段錯(cuò)誤也就不奇怪了。
        也就是說指針變量里存儲的地址值并不是一個(gè)我們需要的值,為了指向一塊內(nèi)存空間,因此需要采用malloc分配一塊內(nèi)存空間。
        改寫上面的代碼實(shí)現(xiàn)內(nèi)存的分配。
        int main()
        {
        /*創(chuàng)建一塊內(nèi)存空間,并讓stu.name指向這塊內(nèi)存空間*/
        stu.name = (char *)malloc(20*sizeof(char));
        /*實(shí)現(xiàn)字符串的復(fù)制過程*/
        strcpy(stu.name,"Jimy");
        stu.score = 99;
        /*創(chuàng)建一塊內(nèi)存空間,并讓pstu指向這塊內(nèi)存空間*/
        pstu = (struct student *)malloc(sizeof(struct student));
        /*創(chuàng)建一塊內(nèi)存空間,并讓pstu->name指向這塊內(nèi)存空間*/
        pstu->name = (char *)malloc(20*sizeof(char));
        /*實(shí)現(xiàn)字符串的復(fù)制過程*/
        strcpy(pstu->name,"Jimy");
        pstu->score = 99;
        return 0;
        }
        這樣補(bǔ)充以后的代碼就為指針變量添加了指向的對象,由于是采用malloc動態(tài)申請的存儲空間,那么這段存儲空間是分配在堆中,而不是在棧中,如果是在被調(diào)用函數(shù)中申請內(nèi)存空間,那么在函數(shù)返回后該內(nèi)存空間并不會釋放。
        Breakpoint 1, main () at TestStructPoint.c:21
        21 stu.name = (char *)malloc(20*sizeof(char));
        Missing separate debuginfos, use: debuginfo-install glibc-2.12.90-17.i686
        (gdb) p stu ----stu中的內(nèi)容
        $1 = {name = 0x0, score = 0}
        (gdb) p stu.name ----stu.name其中的內(nèi)容是0x0,也就是指向0x0
        $2 = 0x0
        (gdb) c
        Continuing.
        Breakpoint 2, main () at TestStructPoint.c:25
        25 strcpy(stu.name,"Jimy");
        (gdb) p stu.name -----stu.name其中的內(nèi)容不再是0x0,而是一個(gè)地址值,該地值中的內(nèi)容為空
        $3 = 0x804a008 ""
        (gdb) c
        Continuing.
        Breakpoint 3, main () at TestStructPoint.c:26
        26 stu.score = 99;
        (gdb) p stu.name -----stu.name中存儲的地址的內(nèi)容發(fā)生了變化。
        $4 = 0x804a008 "Jimy"
        (gdb) c
        Continuing.
        Breakpoint 4, main () at TestStructPoint.c:29
        29 pstu = (struct student *)malloc(sizeof(struct student));
        (gdb) p pstu ----pstu指向的地址也是0x0
        $5 = (struct student *) 0x0
        (gdb) c
        Continuing.
        Breakpoint 5, main () at TestStructPoint.c:32
        32 pstu->name = (char *)malloc(20*sizeof(char));
        (gdb) p pstu ----pstu指向的內(nèi)存地址發(fā)生了改變,不再是0x0
        $6 = (struct student *) 0x804a020
        (gdb) c
        Continuing.
        Breakpoint 6, main () at TestStructPoint.c:35
        35 strcpy(pstu->name,"Jimy");
        (gdb) p pstu
        $7 = (struct student *) 0x804a020
        (gdb) p pstu->name ----pstu->name中的地址也不再是0x0,而是一個(gè)非零的地址值
        $8 = 0x804a030 ""
        (gdb) c
        Continuing.
        Breakpoint 7, main () at TestStructPoint.c:36
        36 pstu->score = 99;
        (gdb) p pstu->name
        $9 = 0x804a030 "Jimy" ----pstu->name指向地址中的內(nèi)容發(fā)生改變
        (gdb) c
        Continuing.
        Program exited normally.
        根據(jù)上面的調(diào)試可以知道,指針變量在定義過程中沒有初始化為NULL,則指針變量指向的地址就是0x0,而在Linux中的進(jìn)程虛擬存儲器映射中地址0x0并沒有映射,因此會出現(xiàn)錯(cuò)誤。因此結(jié)構(gòu)體中的指針變量一定要指向一塊具體的存儲空間之后才能進(jìn)行相應(yīng)的操作。同時(shí)其他的指針也必須指向相應(yīng)的地址以后再操作。
        但是分配完地址后還需要在相應(yīng)操作結(jié)束后釋放分配的存儲器,不然會造成浪費(fèi),以及內(nèi)存的泄漏。這也是很多程序員忘記完成的工作。
        內(nèi)存的釋放采用free函數(shù)即可,free函數(shù)是將分配的這塊內(nèi)存與指針(malloc返回的指針)之間的所有關(guān)系斬?cái)?,指針變量P中存儲的地址(這塊內(nèi)存的起始地址)值也沒有發(fā)生變化,同時(shí)存儲器中存儲的內(nèi)容也并沒有發(fā)生改變,改變的只是指針對這塊內(nèi)存地址的所有權(quán)問題。但是該起始地址所在內(nèi)存中的數(shù)據(jù)內(nèi)容已經(jīng)沒法使用了,即時(shí)采用其他的指針也不能訪問。如果下一次調(diào)用malloc函數(shù)可能會在剛才釋放的區(qū)域創(chuàng)建一個(gè)內(nèi)存空間,由于釋放以后的存儲空間的內(nèi)容并沒有改變(我是參考書上的,但我認(rèn)為free后存儲器中的內(nèi)容是發(fā)生變化的,后面的調(diào)試可以說明這個(gè)問題,只是不知道發(fā)生什么變化,我也只是猜測,但是不要訪問這個(gè)存儲空間的內(nèi)容是最安全的),這樣可能會影響后面的結(jié)果,因此需要對創(chuàng)建的內(nèi)存空間進(jìn)行清零操作(防止前面的操作影響后面),這通常采用memset函數(shù)實(shí)現(xiàn),具體參看memset函數(shù)。還有指針變量P中存儲的地址值并沒有改變,由于指針P沒有對這個(gè)地址的訪問權(quán)限,程序中對P的引用都可能導(dǎo)致錯(cuò)誤的產(chǎn)生,造成野指針,因此最后還需要將指針P指向NULL,避免野指針的產(chǎn)生。當(dāng)然也需要對創(chuàng)建是否成功需要檢測,但這里我暫時(shí)不考慮這些錯(cuò)誤的處理。
        上一頁 1 2 下一頁

        評論


        技術(shù)專區(qū)

        關(guān)閉
        主站蜘蛛池模板: 怀集县| 海城市| 洛扎县| 邻水| 二手房| 金华市| 分宜县| 页游| 宣威市| 宁陵县| 夏河县| 铅山县| 西充县| 玉屏| 建宁县| 郯城县| 安岳县| 建水县| 眉山市| 舞阳县| 博爱县| 罗田县| 林口县| 抚顺市| 大邑县| 南漳县| 南和县| 塔城市| 长顺县| 黄冈市| 乌审旗| 聂拉木县| 绥阳县| 吉水县| 克什克腾旗| 永昌县| 仙游县| 宁陕县| 万荣县| 连江县| 新乡市|