内存分配

内存分配的过程中容易出现以下的错误:

   1,内存未分配成功,却在程序中使用了,解决办法是:使用前先进行判断

   2,内存分配成功后,未进行初始化,解决办法是:初始化为常量值

   3,内存分配成功后,初始化后,对内存的使用超过了边界。解决办法:注意边界条件

   4,使用分配的内存后忘记释放内存。解决办法:释放与申请相对应。

   5,释放后继续使用内存,解决办法:释放后将指向内存的指针赋值为NULL

c语言中的内存分配:

       C语言中采用malloc()和free()函数进行内存的分配和释放,当然还有其他内存分配的函数(calloc()和realloc())。此文只对malloc()和free()函数进行了讨论。

     calloc()函数:void* calloc(size_t num, size_t size_of_element)

                       说明:    其中num为元素的个数,size_of_element为每个元素所占的空间大小,申请的空间大小为:num*size_of_element,  同时每个空间被初始化为零。

#include<stdio.h>
#include<stdlib.h>
int main(void)
{
	int i;
	int *pn=(int *)calloc(10,sizeof(int));
	for(i=0;i<10;i++)
		printf("%3d",*pn++);
	printf("\n");
	free(pn);
	return 0;
} 

      realloc()函数: void*  realloc(void* mem_address, size_t newsize)

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int i;
	int *pn=(int *)malloc(5*sizeof(int));
	printf("%p\n",pn);
	for(i=0;i<5;i++)
		scanf("%d",&pn[i]);
	pn=(int *)realloc(pn,10*sizeof(int));
	printf("%p\n",pn);
	for(i=0;i<5;i++)
		printf("%3d",pn[i]);
	printf("\n");
	free(pn);
	return 0;
} 
                      说明:  按照newsize的大小申请一个空间,将原来数据从地址mem_address中拷贝到新的地方,释放原来的内存,同时返回指向新的内存的指针。 realloc失败的时候,返回NULL;   realloc失败的时候,原来的内存不改变,不会释放也不会移动;传递给realloc的指针必须是先前通过malloc(), calloc(), 或realloc()分配的;   假如原来的内存后面还有足够多剩余内存的话,realloc的内存=原来的内存+剩余内存,realloc还是返回原来内存的地址; 假如原来的内存后面没有足够多剩余内存的话,realloc将申请新的内存,然后把原来的内存数据拷贝到新内存里,原来的内存将被free掉,realloc返回新内存的地址;   如果size为0,效果等同于free()。这里需要注意的是只对指针本身进行释放,例如对二维指针**a,对a调用realloc时只会释放一维,使用时谨防内存泄露。

一、malloc()和free()的基本概念以及基本用法:

1、函数原型及说明:
         void *   malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针。如果分配失败,则返回一个空指针(NULL)。在写代码的过程一定要对内存分配失败与否进行判断。

         void      free(void *FirstByte):该函数是将之前用malloc分配的空间还给程序或者是操作系统,也就是释放了这块内存,让它重新得到自由。

        char *pChar = NULL;
	pChar = (char *)malloc(10*sizeof(char));
	if (NULL == pChar)   //check the malloc()'s return
	{
		cout<<"memory failed!"<<endl;
		exit(1);
	}
	//do something 
	free(pChar);
        pChar = NULL;


2     关于函数使用需要注意的一些地方:       A、申请了内存空间后,必须检查是否分配成功。       B、当不需要再使用申请的内存时,记得释放;释放后应该把指向这块内存的指针指向NULL,防止程序后面不小心使用了它。       C、这两个函数应该是配对。如果申请后不释放就是内存泄露;如果无故释放那就是什么也没有做。释放只能一次,如果释放两次及两次以上会出现错误(释放空指针例外,释放空指针其实也等于啥也没做,所以释放空指针释放多少次都没有问题)。        D、虽然malloc()函数的类型是(void *),任何类型的指针都可以转换成(void*),但是最好还是在前面进行强制类型转换,因为这样可以躲过一些编译器的检查。3    深层次理解malloc()和free()

3.1、malloc()到底从哪里得到了内存空间?从堆里面获得空间。也就是说函数返回的指针是指向堆里面的一块内存。操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时,就会遍历该链表,然后就寻找合适的空间(不同的内存管理办法采用不同的分配原则),然后就将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。就是这样!(具体的怎么去分配的可以看有关OS的书中内存管理的相关章节,内存管理有:分页式管理,分段式管理和两种方式结合)     

 3.2、什么是堆:堆是大家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。什么是栈:栈是线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立。每个函数都有自己的栈,栈被用来在函数之间传递参数。操作系统在切换线程的时候会自动的切换栈,就是切换SS/ESP寄存器。栈空间不需要在高级语言里面显式的分配和释放。

栈是由编译器自动分配释放,存放函数的参数值、局部变量的值等。操作方式类似于数据结构中的栈。堆一般由程序员分配释放,若不释放,程序结束时可能由OS回收。注意这里说是可能,并非一定。所以我想再强调一次,记得要释放!注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。所以,举个例子,如果你在函数上面定义了一个指针变量,然后在这个函数里申请了一块内存让指针指向它。实际上,这个指针的地址是在栈上,但是它所指向的内容却是在堆上面的!这一点要注意!所以,再想想,在一个函数里申请了空间后,比如说下面这个函数:程序代码:

void Function(void)
{
        char *p = (char *)malloc(100 * sizeof(char));
}




就这个例子,千万不要认为函数返回,函数所在的栈被销毁指针也跟着销毁,申请的内存也就一样跟着销毁了!这绝对是错误的!因为申请的内存在堆上,而函数所在的栈被销毁跟堆完全没有啥关系。所以,还是那句话:记得释放
         3.3、free()到底释放了什么

         free()释放的是指针指向的内存!注意!释放的是内存,不是指针!这点非常非常重要!指针是一个变量,只有程序结束时才被销毁。释放了内存空间后,原来指向这块空间的指针还是存在!只不过现在指针指向的内容的垃圾,是未定义的,所以说是垃圾。因此,前面我已经说过了,释放内存后把指针指向NULL,防止指针在后面不小心又被解引用了。非常重要啊这一点!

  
	free(pChar);
        pChar = NULL;    //赋值为NULL

4   malloc()以及free()的机制:


        仔细看一下free()的函数原型,也许也会发现似乎很神奇,free()函数非常简单,只有一个参数,只要把指向申请空间的指针传递
给free()中的参数就可以完成释放工作!这里要追踪到malloc()的申请问题了。申请的时候实际上占用的内存要比申请的大。因为超出的空间是用来记录对这块内存的管理信息。先看一下在《UNIX环境高级编程》中第七章的一段话:

大多数实现所分配的存储空间比所要求的要稍大一些,额外的空间用来记录管理信息——分配块的长度,指向下一个分配块的指针等等。这就意味着如果写过一个已分配区的尾端,则会改写后一块的管理信息。这种类型的错误是灾难性的,但是因为这种错误不会很快就暴露出来,所以也就很难发现。将指向分配块的指针向后移动也可能会改写本块的管理信息。

以上这段话已经给了我们一些信息了。malloc()申请的空间实际我觉得就是分了两个不同性质的空间。一个就是用来记录管理信息的空间,另外一个就是可用空间了。而用来记录管理信息的实际上是一个结构体。在C语言中,用结构体来记录同一个对象的不同信息是
下面看看这个结构体的原型:
程序代码:

struct mem_control_block {
           int is_available;  
           int size;//这是实际空间的大小
     };





对于size,这个是实际空间大小。

所以,free()就是根据这个结构体的信息来释放malloc()申请的空间!而结构体的两个成员的大小我想应该是操作系统的事了。但是这里有一个问题,malloc()申请空间后返回一个指针应该是指向第二种空间,也就是可用空间!不然,如果指向管理信息空间的话,写入的内容和结构体的类型有可能不一致,或者会把管理信息屏蔽掉,那就没法释放内存空间了,所以会发生错误!
好了!下面看看free()的源代码,我自己分析了一下,觉得比起malloc()的源代码倒是容易简单很多。
程序代码:
void free(void *ptr)
{
struct mem_control_block *free;
free = ptr - sizeof(struct mem_control_block);
free->is_available = 1;
return;
}

c++语言中的new和delete运算符

注意此处的new和delete是运算符而不是函数。c++中如果使用malloc()和free(),不能调用构造函数和析构函数,这破坏了空间分配、初始化的功能。所以引入了new和delete。

new的用法



1.     开辟单变量地址空间

         1)new int;  //开辟一个存放数组的存储空间,返回一个指向该存储空间的地址.int *a = new int 即为将一个int类型的地址赋值给整型指针a. 

         2)int *a = new int(5) 作用同上,但是同时将整数赋值为5

2.     开辟数组空间

          一维: int *a = new int[100];开辟一个大小为100的整型数组空间

          二维: int **a = new int[5][6]

          三维及其以上:依此类推.

         一般用法: new 类型 [初值]

delete用法:

          1. int *a = new int;

               delete a;   //释放单个int的空间

          2.int *a = new int[5];

               delete [] a; //释放int数组空间

          要注意new和delete是一对,new[]和delete[]是一对。

          要访问new所开辟的结构体空间,无法直接通过变量名进行,只能通过赋值的指针进行访问.

          用new和delete可以动态开辟,撤销地址空间.在编程序时,若用完一个变量(一般是暂时存储的数组),下次需要再用,但却又想省去重新初始化的功夫,可以在每次开始使用时开辟一个空间,在用完后撤销它.








  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值