硬件层次:内存结构管理
内核层次:内存映射,堆扩展
语言层次:C的malloc以及C++的new等
数据结构层次:智能指针,STL
#include <stdio.h>
main()
{
int *p1 = malloc(4);
int *p2 = malloc(4);
int *p3 = malloc(4);
int *p4 = malloc(4);
printf("p1: %p\n", p1);
printf("p2: %p\n", p2);
printf("p3: %p\n", p3);
printf("p4: %p\n", p4);
}
编译运行:
gcc test1.c -o test1
./test1
结果:
p1: 0x845f008
p2: 0x845f018
p3: 0x845f028
p4: 0x845f038
问题:我们明明申请的是4个字节的空间,但是我们可以看到,相邻的两个变量之间相差16个字节,这是为什么?
我们先看看linux对进程内存的结构描述!
在linux中,当我们运行一个进程时,/proc/${pid}中会存放pid对应的进程的运行信息,包括内存结构,一旦进程结束,该文件夹就会被删除。
使用命令”ps aue“ 可查看当前正在执行的所有进程的信息。
在C中,我们可以如下获取:
#include <stdio.h>
#include <unistd.h> //unix标准库
main()
{
int pid = getpid();
printf("pid: %d\n", pid);
}
好了,接下来我们执行下面这个代码,写个死循环让进程保持活动状态,然后看看其内存结构:
#include <stdio.h>
#include <unistd.h> //unix标准库
main()
{
int pid = getpid();
printf("pid: %d\n", pid);
while(1);
}
得到pid: 3467
cd /proc/3467
该文件夹下是该进程的所有信息,其内存信息存放在maps中:
cat maps
结果:
08048000-08049000 r-xp 00000000 08:0a 8128164 /home/jiange/linuxprogram/day1/test2
08049000-0804a000 r--p 00000000 08:0a 8128164 /home/jiange/linuxprogram/day1/test2
0804a000-0804b000 rw-p 00001000 08:0a 8128164 /home/jiange/linuxprogram/day1/test2
b75a6000-b75a7000 rw-p 00000000 00:00 0
b75a7000-b774f000 r-xp 00000000 08:07 263264 /lib/i386-linux-gnu/libc-2.19.so
b774f000-b7751000 r--p 001a8000 08:07 263264 /lib/i386-linux-gnu/libc-2.19.so
b7751000-b7752000 rw-p 001aa000 08:07 263264 /lib/i386-linux-gnu/libc-2.19.so
b7752000-b7755000 rw-p 00000000 00:00 0
b776b000-b776e000 rw-p 00000000 00:00 0
b776e000-b7770000 r--p 00000000 00:00 0 [vvar] b7770000-b7772000 r-xp 00000000 00:00 0 [vdso] b7772000-b7792000 r-xp 00000000 08:07 263240 /lib/i386-linux-gnu/ld-2.19.so b7792000-b7793000 r--p 0001f000 08:07 263240 /lib/i386-linux-gnu/ld-2.19.so b7793000-b7794000 rw-p 00020000 08:07 263240 /lib/i386-linux-gnu/ld-2.19.so bfd18000-bfd39000 rw-p 00000000 00:00 0 [stack]
里面其他部分我们先不做探究,我们只看4个部分:
代码空间
堆
全局栈
局部栈
而这4个部分对应的是上面的哪些呢?可以自己写代码测试一下!
#include<unistd.h>
#include<malloc.h>
int a1;
static int a2=2;
const int a3=2;
int add(int a,int b)
{
return a+b;
}
int main()
{
int b1;
static int b2=2;
const int b3=2;
int *p1 = (int *)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("f %p\n",add);
printf("%d\n",getpid());
while(1);
return 0;
}
结果:
a1 0x804a034
a2 0x804a028
a3 0x8048620
b1 0xbf919150
b2 0x804a02c
b3 0xbf919154
p1 0x807a008
main 0x804848a
f 0x804847d
3633
我们看一下/proc/3633/maps:
08048000-08049000 r-xp 00000000 08:0a 8128164 /home/jiange/linuxprogram/day1/test2
08049000-0804a000 r--p 00000000 08:0a 8128164 /home/jiange/linuxprogram/day1/test2
0804a000-0804b000 rw-p 00001000 08:0a 8128164 /home/jiange/linuxprogram/day1/test2
0807a000-0809b000 rw-p 00000000 00:00 0 [heap]
b75db000-b75dc000 rw-p 00000000 00:00 0
b75dc000-b7784000 r-xp 00000000 08:07 263264 /lib/i386-linux-gnu/libc-2.19.so
b7784000-b7786000 r--p 001a8000 08:07 263264 /lib/i386-linux-gnu/libc-2.19.so
b7786000-b7787000 rw-p 001aa000 08:07 263264 /lib/i386-linux-gnu/libc-2.19.so
b7787000-b778a000 rw-p 00000000 00:00 0
b77a0000-b77a3000 rw-p 00000000 00:00 0
b77a3000-b77a5000 r--p 00000000 00:00 0 [vvar]
b77a5000-b77a7000 r-xp 00000000 00:00 0 [vdso]
b77a7000-b77c7000 r-xp 00000000 08:07 263240 /lib/i386-linux-gnu/ld-2.19.so
b77c7000-b77c8000 r--p 0001f000 08:07 263240 /lib/i386-linux-gnu/ld-2.19.so
b77c8000-b77c9000 rw-p 00020000 08:07 263240 /lib/i386-linux-gnu/ld-2.19.so
bf8fa000-bf91b000 rw-p 00000000 00:00 0 [stack]
a3,main,add存放在08048000-08049000,为代码空间;(拥有x权限)
a1,a2,b2存放在0804a000-0804b000这一段,为全局栈;
p1存放在0807a000-0809b000,为堆;
b1,b3存放在bf8fa000-bf91b000,为局部栈;
于是可以知道:
全局的普通变量、静态变量放在全局栈中,全局 const变量放在代码区 。
局部普通变量 和const变量都放在局部栈中,static变量放在全局栈中。
malloc申请的空间放在堆中。
main函数 和 自己写的函数 都是放在代码区的。
我们的代码是怎么加载到代码空间的呢?
ldd ./test2
linux-gate.so.1 => (0xb77bd000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75f3000)
/lib/ld-linux.so.2 (0xb77bf000)
上面有一个/lib/ld-linux.so.2,实际上,我们的程序就是通过它来加载到代码空间的!
所以我们想执行程序可以通过以下方式:
/lib/ld-linux.so.2 ./test2
#include<unistd.h>
#include <malloc.h>
int main(int argc, const char *argv[])
{
int a1 = 10;
int a2 = 10;
int a3 = 10;
int *p1 = (int*)malloc(4);
int *p2 = (int*)malloc(4);
int *p3 = (int*)malloc(4);
printf("a1 %p\n",&a1);
printf("a2 %p\n",&a2);
printf("a3 %p\n",&a3);
printf("p1 %p\n",p1);
printf("p2 %p\n",p2);
printf("p3 %p\n",p3);
printf("%d\n",getpid());
while(1);
return 0;
}
结果:
a1 0xbf8eab48
a2 0xbf8eab4c
a3 0xbf8eab50
p1 0x937d008
p2 0x937d018
p3 0x937d028
奇怪的问题来了!a放在栈中,用了4个字节,p放在堆中,用了16个字节!那么,为什么堆多花去了12个字节呢?
我们做以下测试:
#include<malloc.h>
int main(int argc, const char *argv[])
{
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) = 10;
*(p1+9) = 11;
printf("%d\n",*p2);
return 0;
}
结果:
5
没有报错,结果也貌似正确,(p1+4)的地址刚好是p2的地址,答案是5。
我们在结束之前加上一句:
free(p1);
再次执行,程序崩溃了!!
原因:堆其实是由链表管理的,malloc 生成的空间,先存放本身的数据,接下来存放前一个元素的地址,再是后一个元素的地址,再后一个是数据本身的长度。而上面修改了p1+1, p1+2, p1+3,破坏了链表的结构,于是在free的时候,链表无法正确地连接起来!
new是用malloc实现的,delete是用free实现的,只是new 在生成时会初始化(基本类型默认值,UDT调用构造函数),delete在删除前会先清0(或者调用析构函数)。
new []与new: new[]会循环依次调用构造器,new只调用一次。
1.定位分配
char p[20];
int *p1 = new(p) int;
//p1定位分配到p的位置,c预言中对应的有realloc函数。
2.
c语言是弱类型语言,c++是强类型语言
int *p = malloc(4);
在c是对的,在c++是错的,c++一定要(int*)malloc(4);
gcc xxx.cpp 是按照c++ 编译的
gcc xxx.c 才会按照c编译