• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 200, comments - 8, trackbacks - 0, articles - 0
            1.malloc怎么分配空間

                malloc與new的關系

                看完下面的2再回答這個問題。

            2. linux對內(nèi)存的結(jié)構(gòu)描述

                a)         /proc/${pid}/         存放進程運行時候所有的信息。程序一結(jié)束,該目錄就刪掉了。

                b)        任何一個程序的內(nèi)存空間其實分成4個基本部分。

                                    i.              代碼區(qū)

                                    ii.              全局棧區(qū)

                                   iii.              堆

                                   iv.              局部棧

            小實驗: 運行一個只包含while(1);的程序,然后另起一個終端,cd /proc下面的對應進程的pid目錄,cat maps,查看到運行進程的內(nèi)存空間分配情況。

            進程查看: ps aue

                c)         理解程序的變量與內(nèi)存空間的關系

            小實驗:

            #include <stdio.h>
            #include <stdlib.h>
            #include <unistd.h>
            int add(int a, int b)
            {
                return a+b;
            }
            int a1 = 1;
            static int a2 = 2;
            const int a3 = 3;
            main()
            {
                int b1 = 4;
                static b2 = 5;
                const b3 = 6;
                
                int *p1 = malloc(4);
                
                printf("a1:%p\n", &a1);
                printf("a2:%p\n", &a2);
                printf("a3:%p\n", &a3);
                printf("b1:%p\n", &b1);
                printf("b2:%p\n", &b2);
                printf("b3:%p\n", &b3);
                printf("p1:%p\n", p1);
                printf("main:%p\n", main);
                printf("add:%p\n", add);
                
                printf("%d\n", getpid());
                while(1);
            }

             

            把打印結(jié)果與/proc下對應目錄中的maps文件比較。

            (代碼區(qū)一般是ox8048000開頭的區(qū)域。 )

            可以看到 a3全局常量在代碼區(qū)(字面值神馬的也是放在代碼區(qū))。 b3局部常量放在局部棧區(qū)。

            a1, a2, b2 則是放在全局棧區(qū)。

            main, add 在代碼區(qū)。

            b1, b3在局部棧區(qū)。

            p1 在堆

             

            小實驗:

            #include <stdio.h>
            #include <stdlib.h>
            #include <unistd.h>

            main()
            {
                int a1 = 10;
                int a2 = 20;
                int a3 = 30;
                
                int *p1 = malloc(4);
                int *p2 = malloc(4);
                int *p3 = malloc(4);
                
                printf("%p\n", &a1);
                printf("%p\n", &a2);
                printf("%p\n", &a3);
                printf("%p\n", p1);
                printf("%p\n", p2);
                printf("%p\n", p3);
                printf("%p\n", &a1);
                printf("%p\n", &a1);
                printf("%p\n", &a1);
                
                printf("%d\n", getpid());
                while(1);
            }
            復制代碼

            運行結(jié)果如下:

            可以看到,a1, a2, a3的地址降序排列,相差4個字節(jié)。(棧  分配內(nèi)存是直接壓到棧頂)

            p1, p2, p3的地址升序排列,相差16個字節(jié)。(堆)

            小結(jié):

                 (1)內(nèi)存分四個區(qū)。
                 (2)各種變量對應存放區(qū)。
                 (3)堆棧是一種管理內(nèi)存的數(shù)據(jù)結(jié)構(gòu)。

            查看程序的內(nèi)存地址。

             

            回到問題1.

            看一個小實驗:

             #include <stdio.h>

            #include <stdlib.h>

            int main()
            {
                int *p1 = malloc(4);
                int *p2 = malloc(4);
                int *p3 = malloc(4);
                
                *p1 = 1;
                *(p1+1) = 2;
                *(p1+2) = 3;    
                *(p1+3) = 4;
                *(p1+4) = 5;
                *(p1+5) = 6;
                *(p1+6) = 7;
                *(p1+7) = 8;
                *(p1+8) = 9;
                
                printf("%d\n", *p2);
                return 0;
            }

            運行結(jié)果是5.

            如果在程序中加一句話后:


            #include <stdio.h>
            #include <stdlib.h>

            int main()
            {
                int *p1 = malloc(4);
                int *p2 = malloc(4);
                int *p3 = malloc(4);
                
                *p1 = 1;
                *(p1+1) = 2;
                *(p1+2) = 3;    
                *(p1+3) = 4;
                *(p1+4) = 5;
                *(p1+5) = 6;
                *(p1+6) = 7;
                *(p1+7) = 8;
                *(p1+8) = 9;
                
                free(p1);    //比上面的程序多了這句話
                printf("%d\n", *p2);
                return 0;
            }

             

            則會發(fā)生錯誤。

            p1指向int型,應該只占用4個字節(jié)。可是實際上卻占了16個字節(jié),因為P1其實是鏈表里的一個節(jié)點,多的12個字節(jié)其實是保存的一些指向下一個節(jié)點,或者別的一些信息。我們在用*(p1+1) = 2;   *(p1+2) = 3;          *(p1+3) = 4;破壞這些信息的時候,不會報錯,但是在使用這個節(jié)點(free(p))時,則會報錯了。

            3. 理解malloc的工作原理

            malloc使用一個數(shù)據(jù)結(jié)構(gòu)(鏈表)來維護分配的空間。鏈表的構(gòu)成:分配的空間、上一個空間的地址、下一個空間的地址、以及本空間的信息等。對malloc分配的空間不要越界訪問,因為容易破壞后臺的鏈表維護結(jié)構(gòu),導致malloc/free/calloc/realloc不正常工作。

            4. C++的new與malloc的關系

            小實驗:


            #include <stdio.h>
            #include <stdlib.h>
            #include <unistd.h>

            int main()
            {
                int *p1 = (int*)malloc(4);
                int *p2 = new int;
                int *p3 = (int *)malloc(4);
                int *p4 = new int;
                int *p5 = new int;
                int *p6 = new int;
                
                printf("%p\n", p1);
                printf("%p\n", p2);
                printf("%p\n", p3);
                printf("%p\n", p4);
                printf("%p\n", p5);
                printf("%p\n", p6);
                return 0;
            }

            運行結(jié)果:

            結(jié)論:new的實現(xiàn)使用的是malloc來實現(xiàn)的。

            區(qū)別:new使用malloc后,還要初始化空間。基本類型,直接初始化成默認值。 UDT類型調(diào)用指定的構(gòu)造器

            推論:delete也是調(diào)用free實現(xiàn)。

            區(qū)別:delete會調(diào)用指定的析構(gòu)器,然后再調(diào)用free()。

            new與new[]的區(qū)別:new只調(diào)用一個構(gòu)造器初始化。new[]循環(huán)對每個區(qū)域調(diào)用構(gòu)造器。

            delete與delete[]的區(qū)別:delete只調(diào)用一次析構(gòu)函數(shù),而delete則把數(shù)組中的每個對象的析構(gòu)函數(shù)都調(diào)用一遍。

            malloc      new

            realloc      new() //定位分配

            calloc       new[]

            free          delete

            5. 函數(shù)調(diào)用棧空間分配與釋放

            5.1 總結(jié):

            1. 函數(shù)執(zhí)行的時候有自己的臨時棧。(C++中的成員函數(shù)有對象棧空間和函數(shù)棧空間兩個空間)
            2. 函數(shù)的參數(shù)就在臨時棧中。如果函數(shù)傳遞實參,則用來初始化臨時參數(shù)變量。
            3. 通過寄存器返回值(使用返回值返回數(shù)據(jù))
            4. 通過參數(shù)返回值(參數(shù)必須是指針。指針指向的區(qū)域必須事先分配)
            5. 如果參數(shù)返回指針,參數(shù)就是雙指針。

            5.2 __stdcall, __cdecl __fastcall的問題(了解,應付面試即可)


            #include <stdio.h>
            int _attribute_((stdcall)) add(int *a, int *b)
            {
                return *a+*b;
            }

            int main()
            {
                int a1 = 20;
                int b2 = 30;
                int r = add(&a, &b);
                
                printf("%d\n", r);
            }

             

            1. 這三個屬性決定函數(shù)參數(shù)壓棧順序。都是從右到左。
            2. 決定函數(shù)棧清空的方式。是調(diào)用者清空還是被調(diào)用者清空
            3. 決定了函數(shù)的名字轉(zhuǎn)換方式。(編譯的時候,會把函數(shù)重新命名。)

            6. far near huge指針的問題(linux中不考慮這個問題。window中屬于遺留問題。windows編程統(tǒng)一采用far指針)

                near  16

                far    32

                huge 綜合

             

              Note: C與C++明顯的不同表現(xiàn)在 引用, 模板, 異常以及面向?qū)ο?/span>

                          函數(shù)參數(shù)傳值和傳指針其實是一樣的,只是一個是把值拷貝過去,一個是把地址拷貝過去。

             

             

            7.虛擬內(nèi)存

            小實驗:寫一個程序,定義一個整型指針,賦值為999,打印出它的地址,同時while(1)讓它一直運行著。再寫一個程序,定義一個整形指針,直接指向剛才打印出來的地址,然后打印這個指針指向的整數(shù),為打印出999嗎?  (不會,段錯誤)

            問題:

            為什么一個程序不能訪問另外一個程序的地址指向的空間?

            理解:

            1. 每個程序的開始地址一般都是0x80084000。
            2. 由1可以看出程序中使用的地址不是物理地址,而是邏輯地址(虛擬內(nèi)存)。邏輯地址僅僅是個編號,使用int 4字節(jié)整數(shù)表示。(4字節(jié)所能表示的最大整數(shù)是2的32次方=4294967296=4G)。所以每個程序提供了4G的訪問能力

            問題:

                   邏輯地址和物理地址怎么關聯(lián)?(內(nèi)存映射)

            背景:

                   虛擬內(nèi)存的提出:禁止用戶直接訪問物理存儲地址。有助于系統(tǒng)的穩(wěn)定。

            結(jié)論:

                   虛擬地址與物理地址在映射的時候有一個基本單位4k(16進制的1000,稱為內(nèi)存頁)。

                   段錯誤:無效訪問。虛擬地址與物理地址沒有映射。

                   沒有段錯誤不一定是合法訪問。

                   合法訪問:比如malloc分配的空間之外的空間(malloc后就映射了)可以訪問但是訪問非法。int *p1 = malloc(4);   int *(p1+12) = 233;  第二句不會報段錯誤,但是是非法訪問。

             

             

             

            8.虛擬內(nèi)存的分配

                   棧:編譯器自動生成代碼維護

                   堆:地址是否映射?映射的空間是否被管理?

            1. brk/sbrk內(nèi)存映射函數(shù)

              補充:幫助文檔:man 節(jié) 關鍵字 

              節(jié):1-8    1: Linux系統(tǒng)(shell)指令  (ls等)

                            2: 系統(tǒng)函數(shù)  (brk等)

                            3: 標準C函數(shù)的幫助文檔  (fopen等)

                            7: 系統(tǒng)的編程幫助  (tcp, icmp等)

             

                   分配釋放內(nèi)存

                   int brk(void *addr);  //分配空間,釋放空間

                   void *sbrk(int size);  //返回指定大小的空間的地址

                   應用:

            1. 使用sbrk分配內(nèi)存空間    int *p = sbrk(4);  //分配4字節(jié)整數(shù)
            2. 使用sbrk得到?jīng)]有映射的虛擬地址  int *p1 = sbrk(0);  //返回沒有映射的虛擬地址的首地址,不能給*p1賦值,會出現(xiàn)段錯誤。因為還沒有映射。
            3. 使用brk分配空間
            4. 使用brk釋放空間

            理解:

                   sbrk(int size)

                   sbrk與brk后臺系統(tǒng)維護一個指針。指針默認是null。

                   調(diào)用sbrk,判定指針是否是0(第一次調(diào)用),如果是:得到大塊空閑地址的首地址來初始化該指針。返回該指針給指針變量賦值,同時把指針指向+size的地方。如果是否:返回指針,并且將指針位置+size。

            #include <stdio.h>
            #include <unistd.h>

            int main()
            {
                int *p = sbrk(0);  //返回空閑地址,并修改指針為+size(這里是0,),注意這個指針不是*p,而是sbrk指向內(nèi)存里的指針。
            //這里是0,并且是首次調(diào)用,所以內(nèi)存并沒有映射。如果括號里是4或者4的倍數(shù),則會返回指針的同時做映射,并把sbrk的指針指向+4的位
            //置以便供下一次調(diào)用的時候返回地址。并不是括號里是4就只映射4個字節(jié)的地址,而是映射一頁的內(nèi)存。這是為了效率的考慮。好比吃饅頭,
            //不是吃一個做一個,而是要吃了,做一屜,慢慢吃。所以 *(p+10)= 20; 是可以訪問的(p最多只能加到1023,不然仍然會段錯誤)。
            //但是是非法訪問。
                
                printf("%d\n", *p);
            }

            #include <stdio.h>
            #include <unistd.h>

            int main()
            {
                int *p1 = sbrk(4);  //返回空閑地址,并修改指針為+size
                int *p2 = sbrk(0);
                
                printf("%p\n", p1);
                printf("%p\n", p2);  //通過上面的程序分析,這里打印的
            //是p1加上4個字節(jié)后的地址。int *p2 = sbrk(200);這句話括號里即使
            //是200,p2也是p1加4個字節(jié),因為sbrk是先返回當前的地址,再加括
            //號里的size。如果括號里是負數(shù),則表示釋放空間。
                
                while(1);
            }


            下面再看brk(void *p)函數(shù):


            #include <stdio.h>
            #include <unistd.h>

            int main()
            {
                int *p = sbrk(0);
                brk(p+1);  //將sbrk里面的指針向后移動4個字節(jié),發(fā)現(xiàn)沒
            //有映射,就會自動映射區(qū)域。所以后面的*p就可以訪問了。
                *p = 800;
                brk(p);   //將指針再移回去,相當于釋放內(nèi)存空間,即取
            //消之前的映射。后面再訪問就會出錯了。
                *p = 29;  //段錯誤。
                
                while(1);
            }


            應用案例:

                   寫一個程序查找1-10000之間的所有的素數(shù),并且存放到緩沖,然后打印。

                   分析:1-10000如果用數(shù)組的話,不太現(xiàn)實,有大部分空間都用不上。C++的話可以用鏈表實現(xiàn),但是鏈表的開銷比較大。用malloc和new都不太好。所以,緩沖的實現(xiàn)使用sbrk/brk。

                   流程:

                          判斷是否是素數(shù)(isPrime)

                          是,分配空間存放

                          否,繼續(xù)下步

             #include <stdio.h>

            #include <unistd.h>

            int isPrime(int a)
            {
                int i = 0;
                for(i = 2; i < a; i++)
                {
                    if(a%i == 0)
                        {
                            return 1;
                        }
                }
                return 0;
            }

            int main()
            {
                int i = 2; //循環(huán)變量
                int *r;
                int *p;  //p一直指向頁首
                r = sbrk(0);
                p = r;
                for(; i<10000; i++)
                {
                    if(isPrime(i))
                        {
                            brk(r+1);
                            *r = i;
                            r = sbrk(0);
                        }
                }
                
                i = 0;
                r = p;
                while(r != sbrk(0))
                {
                    printf("%d\n", *r);
                    r++;
                }
                brk(p);  //釋放空間
            }

             

            總結(jié):

                   new   //C++里面用得比較多

                   malloc  //C里面用得比較多,一定要制定空間大小

                   brk/sbrk  //數(shù)據(jù)比較簡單,量比較大的時候用效率比較高

            異常處理

                   int brk(void *)  //返回int值

                   void *sbrk(int)  //返回指針

                   如果成功,brk返回0, sbrk返回指針

                   如果失敗, brk返回-1, sbrk返回(void *)-1


            #include <string.h>
            #include <errno.h>
            #include <stdlib.h>
            #include <unistd.h>
            #include <stdio.h>

            int main()
            {
                void *p = sbrk(1000000000*3);
                if(p == (void *)-1)
                    {
                        printf("error!");
                        perror("Hello"); //打印出錯誤信息
                        printf("%m");  //打印出memory error
                        printf("%s", strerror(errno));
                    }
            }


            以下是一些比較常用的函數(shù):

                   字符串函數(shù)string.h      cstring

                   內(nèi)存管理函數(shù)malloc    memset   mamcmp memcpy…bzero

                   錯誤處理函數(shù)

                   時間函數(shù)

                   類型轉(zhuǎn)換函數(shù)

            作業(yè):

                   找出打印1-10000之間的所有孿生素數(shù)。

            国内精品久久久久久久涩爱| 四虎影视久久久免费观看| 少妇内射兰兰久久| 嫩草影院久久99| 久久久亚洲裙底偷窥综合| 国产高潮国产高潮久久久| 久久噜噜电影你懂的| 无码人妻久久一区二区三区蜜桃| 久久久久久精品免费看SSS| 91精品无码久久久久久五月天| 午夜精品久久影院蜜桃| 国产精品久久久亚洲| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 精品国产VA久久久久久久冰 | 性欧美丰满熟妇XXXX性久久久| 久久最近最新中文字幕大全 | 狠狠色婷婷久久综合频道日韩 | 很黄很污的网站久久mimi色| 国产精品99久久久精品无码 | 成人国内精品久久久久影院VR| 久久精品卫校国产小美女| 国产精品99久久久久久宅男| 亚洲国产另类久久久精品黑人| 手机看片久久高清国产日韩| 久久香蕉国产线看观看99| 色欲综合久久中文字幕网| 久久天天躁狠狠躁夜夜avapp | 欧美伊人久久大香线蕉综合 | 亚洲人成电影网站久久| 色综合久久天天综合| 91精品国产综合久久精品| 浪潮AV色综合久久天堂| 久久无码人妻一区二区三区午夜 | 伊人久久大香线蕉综合影院首页| 无码8090精品久久一区 | 日本高清无卡码一区二区久久| 久久国产精品视频| 久久精品亚洲欧美日韩久久| 久久精品一区二区影院| 午夜精品久久影院蜜桃| 国产成人精品三上悠亚久久|