内存管理(从底层到高层):
硬件层次
内存结构管理
内核层次
内存映射
堆扩展
语言层次
c::malloc
c++:new
数据结构
STL(Standard Template Library,标准模板库)
智能指针
例1
运行:编辑malloc.c
执行:gcc malloc.c -o malloc#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); }
结果:
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 // 全局静态变量--在全局区
a2:0x80497e8 // 全局静态变量--在全局区
a3:0x80485f4
//
全局常量--------在代码区
b1:0xbfa06c28 // 局部变量--------在局部栈
b2:0x80497ec // 局部静态变量--在全局区
b3:0xbfa06c24 // 局部常量--------在局部栈
p1:0x84c1008 //指针--------------在堆
main:0x8048432 // 在代码区
add:0x8048424 // 在代码区
4710
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]/栈
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
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]
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]
结论:
- 内存分为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
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 总结
- 函数执行的时候有自己的临时栈空间。
- 函数的参数就在临时栈中,如果函数传递实参,则用来初始化临时参数变量。
- 通过寄存器返回值。(使用返回值返回数据)
- 通过参数返回值。(参必须是指针,指针指向的区域必须事先分配)
- 如果参数返回指针,参数就是双指针。
5.2 __stdcall __cdecl __fastcall
写在函数名前:linux:__attribute__((stdcall))
windows:__stdcall
1.决定含糊栈压栈顺序。从右到左2.决定函数栈清空方式。3.决定了函数的名字转换方式。
同样,delete是调用free实现。