内存分布
1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共 享内存,做进程间通信,子进程堆区的开辟。
3. 堆用于程序运行时动态内存分配,堆是向上增长的。
4. 数据段--存储全局数据和静态数据。
5. 代码段--可执行的代码/只读常量。
在C和C++中我们的函数内变量都是存在于栈上的,当程序开始执行时,系统会在为函数在栈上开辟一块空间以存放函数产生的数据,栈是向下生长的,每当我们开辟一块栈空间,就有一个指针向下移动。
我们编写程序的时候会用到很多的库函数如iostream,vector,string的函数,在整个程序里面这些都只需要一份而且基本上每个程序或者进程需要的都是同一份,在程序执行或者说进程启动时这一份数据就会存放在内存映射段中,映射段内的数据被整个程序所有的线程所共享。
堆区用于我们动态开辟内存使用的,这里的空间是被我们自己开辟,管理和释放的,若是我们使用完后没有主动去释放这块内存就会导致内存泄漏影响程序的效率。
数据段也叫静态区,这是存放全局数据和静态数据的地方(被static修饰的变量)
代码段也叫常量区,可执行的代码存放位置,也是常量的存放地方(如const修饰的变量,平时直接使用的常量"123456789abcd"这些不会改变的都是放在这个地方),这些只是规定的不会改变,其实只要我们能拿到这个位置的地址,通过地址去访问内存的全部数据都是可以修改的。
在操作系统为程序或者说进程分配内存的时候这个表肯定是会复杂很多,并且有相应的算法对应内存的开辟和使用管理,不过在语言层,它也就只需要考虑上图的分配数据管理方式即可。最上层的内核空间就是映射的操作系统的内存地址的空间每个程序都拥有一份是方便程序(进程)与系统的切换,例如需要调用系统接口的时候。
C语言中管理内存的方式
C语言中4个函数用于动态管理内存:malloc/calloc/realloc/free 这四个函数前两个是动态开辟内存的函数
这里我们分别使用malloc和calloc在堆上创建了一块相同大小的空间,malloc的参数是字节数这里一共8字节,calloc的第一个参数代表我们需要创建的空间要存放几个数据,第二个参数代表每一个数据的大小。这两个都是在堆上动态开辟空间的,差异点就是参数和初始化,calloc是会 将开辟的空间内数据都初始化为0,malloc是不理会的,原来里面的数据是怎么样现在还是怎么样。这两个函数的返回值都是一个void*的指针指向开辟好的内存的首地址,因为这里只负责开辟一块规定大小的内存,至于里面存什么数据怎么使用这块内存函数是不管的,所以我们需要对其进行强转为我们需要的类型再进行使用。
realloc函数是用于动态调整开辟后的内存的
这里我是使用realloc将p所指向的空间从原来的8字节改为12字节,若是p所指向的空间后面是有4个字节大小的话就会直接将空间不动向后阔4个字节,若是后面不存在这么大空间的话就会在堆上的其他地方开辟一个连续的12字节的空间再将原来空间的内容拷贝过去。
用以上三个函数开辟空间或者动态调整空间大小都是有机会失败的,若是失败会返回一个NULL空指针,所以正常使用的话是需要对指针进行判断的。
在堆中开辟了空间后这快空间就完全由我们自己管理的,这里我们开辟的内存在后续没有使用需求的时候是需要我们自己去释放这块空间将这块空间交还给操作系统管理的这就是free函数的作用
将指针传递给函数free就会帮我们将空间交换给系统。
C++中的内存管理
C++是兼容C语言的,上述函数在C++中都可以使用,但是大部分时候使用C语言的内存管理函数会有些麻烦,它就只是单纯的给我开辟一段空间,而且失败了也没有任何提示需要我们自己去判断,所以C++也对其进行了改良,做出了new和delete运算符
这是new和delete的基础用法,我们在new后面加个小括号里面是需要存放的数据类型,它会帮我们申请一个int类型数据大小的空间就是4字节,第二个是new后面加个类型再用方括号扩起我们需要的数据个数就会给我们申请4个int类型的空间大小即16个字节,而且不需要我们去强转返回指针类型,可以直接接收。并会再申请失败时报异常。可以再后面进行异常的捕捉。但是new出来的空间是没有对应的函数进行内存的扩充的,当我们需要内存扩充的时候需要手动new一个新空间再将旧空间的值拷贝新空间在delete掉旧空间。
delete就是释放空间这里需要对应单个的对应单个的delete,多个数据的调用delete[]。
自定义类型:
C++中有了自定义类型类,类的初始化需要调用构造函数释放需要调用析构函数,但是C语言中的内存管理函数只会帮我们申请空间和交还空间,所以c++的new和delete更多其实就是为了自定义类型而创造出来的,new和delete根据自定义类型申请空间的时候会调用其对应的构造函数,释放时会调用其析构函数。
operator new 和operator delete
new和delete是用户进行动态内存管理的操作符,operator new 和operator delete是系统提供的全局函数,new和delete操作符就是通过调用operator new 和operator delete来完成内存的动态管理。
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,
尝试执行空
间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
// report no memory
// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void *pUserData)
{
_CrtMemBlockHeader * pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /* block other threads */
__TRY
/* get a pointer to memory block header */
pHead = pHdr(pUserData);
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg( pUserData, pHead->nBlockUse );
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
/*
free的实现
*/
#define
free(p)
_free_dbg(p, _NORMAL_BLOCK
如上代码,调用的operator new 和operator delete函数实际上也是通过malloc申请内存空间,和free释放内存空间的。
定位new表达式
定位new表达式用于对一块原始是内存空间进行显示调用自定义类型的构造函数来初始化
紧接着new的括号里面是指向动态开辟的内存空间的指针,紧跟着A类的括号里面是类的初始化列表,若是由无参构造函数可以省略。