內存結構-堆結構-內存分配函數

內存管理(從底層到高層):

硬件層次

內存結構管理

內核層次

內存映射

堆擴展

語言層次 

c::malloc    

c++:new

數據結構

STL(Standard Template Library,標準模板庫

智能指針


例1

運行:編輯malloc.c

#include <stdio.h>
#include <stdlib.h>
main()
{
	int *p1=malloc(4);
	int *p2=malloc(4);
	int *p3=malloc(4);
	int *p4=malloc(4);
	int *p5=malloc(4);
	printf("%p\n",p1);
	printf("%p\n",p2);
	printf("%p\n",p3);
	printf("%p\n",p4);
	printf("%p\n",p5);
}


執行:gcc malloc.c -o malloc
結果:

0x9066008
0x9066018
0x9066028
0x9066038
0x9066048

問題是:爲什麼開闢4字節空間,而實際上是開闢的空間間距是16字節?


嘗試使用標準c++來寫

#include <stdio.h>
#include <stdlib.h>
int main()
{
        int *p1=new int;
        int *p2=new int;
        int *p3=new int;
        int *p4=new int;
        int *p5=new int;
        printf("%p\n",p1);
        printf("%p\n",p2);
        printf("%p\n",p3);
        printf("%p\n",p4);
        printf("%p\n",p5);
        return 0;
}

執行:g++ malloc.cpp -o new

結果:

0x8b53008
0x8b53018
0x8b53028
0x8b53038
0x8b53048

對比,和c的結果一模一樣。


1.問題:malloc怎麼分配空間?malloc月new的關係?


2.Linux對內存的結構描述

例2:


目錄/proc/${pid}/ 存放進程運行時候所有的信息包括內存結構
exe 指向當前運行的程序
cwd 連接 指向當前程序所在目錄
maps 保存程序運行的所有的內存結構


編輯:

#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);
}



cd  /proc/    
ps aue

運行結果:
a1:0x80497e4 //全局變量--------在全局區
a2:0x80497e8 //全局靜態變量--在全局區
a3:0x80485f4 //全局常量--------在代碼區
b1:0xbfa06c28 //局部變量--------在局部棧
b2:0x80497ec //局部靜態變量--在全局區
b3:0xbfa06c24 //局部常量--------在局部棧
p1:0x84c1008 //指針--------------在堆
main:0x8048432 //在代碼區
add:0x8048424 //在代碼區
4710


查看進程id  進入相應的文件夾   再查看maps
[sh@localhost ~]$ ll /proc/4710/cwd
lrwxrwxrwx. 1 sh SH 0 2月  28 17:53 /proc/4710/cwd -> /home/SH/project/cpp/test

[sh@localhost ~]$ ll /proc/4710/exe
lrwxrwxrwx. 1 sh SH 0 2月  28 17:53 /proc/4710/exe -> /home/SH/project/cpp/test/main

[sh@localhost ~]$ cat /proc/4710/maps

007cf000-007ed000 r-xp 00000000 fd:00 26990      /lib/ld-2.12.so
007ed000-007ee000 r--p 0001d000 fd:00 26990      /lib/ld-2.12.so
007ee000-007ef000 rw-p 0001e000 fd:00 26990      /lib/ld-2.12.so
007f9000-007fa000 r-xp 00000000 00:00 0          [vdso]
00832000-009c3000 r-xp 00000000 fd:00 26991      /lib/libc-2.12.so
009c3000-009c5000 r--p 00191000 fd:00 26991      /lib/libc-2.12.so
009c5000-009c6000 rw-p 00193000 fd:00 26991      /lib/libc-2.12.so
009c6000-009c9000 rw-p 00000000 00:00 0
08048000-08049000 r-xp 00000000 fd:00 17633      /home/SH/project/cpp/test/main 代碼區,x可執行
08049000-0804a000 rw-p 00000000 fd:00 17633      /home/SH/project/cpp/test/main 全局區
084c1000-084e2000 rw-p 00000000 00:00 0          [heap]/堆
b7731000-b7732000 rw-p 00000000 00:00 0
b7743000-b7745000 rw-p 00000000 00:00 0
bf9f3000-bfa08000 rw-p 00000000 00:00 0          [stack]/棧

4K爲1頁

結論:
任何程序的內存空間分成4個基本部分
1.代碼區
2.全局棧區
3.堆
4.局部棧



堆棧的區別

例3:

編輯statckleap.c
#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("%d\n",getpid());
    while(1);
}

結果
0xbf8bfb50|
0xbf8bfb4c |  棧相差4字節
0xbf8bfb48 |
0x8734008 |
0x8734018 |  堆相差10字節
0x8734028 |
3531

查看3531進程:
005a6000-005a7000 r-xp 00000000 00:00 0          [vdso]
007cf000-007ed000 r-xp 00000000 fd:00 26990      /lib/ld-2.12.so
007ed000-007ee000 r--p 0001d000 fd:00 26990      /lib/ld-2.12.so
007ee000-007ef000 rw-p 0001e000 fd:00 26990      /lib/ld-2.12.so
00832000-009c3000 r-xp 00000000 fd:00 26991      /lib/libc-2.12.so
009c3000-009c5000 r--p 00191000 fd:00 26991      /lib/libc-2.12.so
009c5000-009c6000 rw-p 00193000 fd:00 26991      /lib/libc-2.12.so
009c6000-009c9000 rw-p 00000000 00:00 0
08048000-08049000 r-xp 00000000 fd:00 17633      /home/SH/project/cpp/test/main
08049000-0804a000 rw-p 00000000 fd:00 17633      /home/SH/project/cpp/test/main
08734000-08755000 rw-p 00000000 00:00 0          [heap]
b774e000-b774f000 rw-p 00000000 00:00 0
b7760000-b7762000 rw-p 00000000 00:00 0
bf8ad000-bf8c2000 rw-p 00000000 00:00 0          [stack]


結論:

  1. 內存分爲4個區。
  2. 各種變量對應存放區。
  3. 堆棧是一種管理內出的數據結構。
  4. 查看一個程序的內存地址。

3.理解malloc工作原理


例4

編輯mallocstruck.c  (越界訪問)
#include <stdio.h>
#include <stdlib.h>
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;

    printf("%d\n",*p2);
    printf("%d\n",*p3);
}
結果是:
5
0

結論:

malloc使用一個數據結構(鏈表)來維護我們分配的空間,
連標的構成:分配的空間、上個空間數據、下個空間數據、空間大小等信息
讀malloc分配的空間不要越界訪問,應爲容易破壞後臺維護機構,導致 malloc、free、calloc、realloc不正常工作。

4.c++的new與malloc的關係

malloc new new[]
realloc(定位分配) new()
calloc new[]
free delete、delete[]

new的實現使用malloc來實現的。
區別:new使用malloc後還要初始化空間,
基本類型,直接初始化成默認值,
UDT類型(用戶自定義類型:類,結構體),調用指定的構造器

同樣,delete是調用free實現。
區別:delete負責調用析構器,然後再調用free。

new 與new[ ]的區別:
new只調用一個構造器初始化。
new[ ]循環對每個區域調用構造器。

delete 月 delete[]的區別:
例子: Stu *p = new Stu[30];
delete p;調用一個析構函數,再把p指向的空間羣全部釋放。
delete[] p;循環調用每個析構函數,再把p指向的空間羣全部釋放。

5.函數調用棧空間的分配與釋放

5.1 總結

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

5.2 __stdcall  __cdecl  __fastcall

寫在函數名前:linux:__attribute__((stdcall))  
 windows:__stdcall
1.決定含糊棧壓棧順序。從右到左
2.決定函數棧清空方式。
3.決定了函數的名字轉換方式。

同樣,delete是調用free實現。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章