c语言/c++动态内存开辟详解以及差别

目录

1.前言

1.1内存分布

1.2动态内存的分配

2.四个重要的内存函数

2.1malloc和free

2.2calloc

2.3realloc

3.常见错误

3.1对NULL指针进行解引用操作

3.2对动态开辟内存的越界访问

3.3使用free释放非动态开辟的空间

3.4使用free释放了动态开辟内存的一部分

3.5对同一块动态内存开辟的空间多次释放·

3.6动态开辟的空间忘记释放,造成内存泄漏

4.参考

5.与c++的差别(malloc/free和new/delete的异同点)

1.前言

1.1内存分布

c语言中内存大致可以分为六个区域,自上而下:内核空间,栈区,内存映射区,堆区,数据段,代码段

c语言/c++动态内存开辟详解以及差别_第1张图片

例:

int c = 1;//储存在数据段
void Test() {
       static int a = 1;//储存在数据段
       int b = 1;//储存在栈里

       int num[10] = { 1,2,3,4 };//储存在栈里;sizeof(num) = 40
       char char1[] = "abcd";//char1和char1*都储存在栈里;sizeof(char1)=5,strlen(char1) = 4
       char* pchar2 = "abcd";//pchar2在栈里,*pchar在数据段里
       int* ptr1 = (int*)malloc(sizeof(int) * 4);//ptr1储存在栈里,*ptr1在堆里
 //解释上面的代码含义以及为什么需要用free释放:malloc等空间申请都是在堆上进行申请的,所以必须由free来进行释放。对应常见错误3.6(因为对堆上的空间由程序员自己来管理)
        int* ptr2 = (int*)calloc(4, sizeof(int));
        int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
        free(ptr1);
        free(ptr3);
}

1.2动态内存的分配

对于下列代码,申请的空间是固定的

int num=10;       //向内存申请4个字节
int arr[10]={0};  //向内存申请40个字节

上述的开辟的空间的方式的特点:

1,空间开辟的大小是固定的

2,数组在申明时必须指定数组的长度,他所需要的内存在编译时分配。

其问题所在之处:对于数组arr[10],已经申请了40个字节的空间,如果部分没有使用就会造成浪费,或者不够,因此动态内存的分配很重要。

2.四个重要的内存函数

2.1malloc和free

c语言提供了一个动态内存开辟的函数:void* malloc (size_t size);(此函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。

#void是空类型,void*是空类型指针,又叫万能指针,就是该指针能接收任意类型的指针,可以指向任何类型对象,所以不能对空类型指针进行解引用,必须强制类型转换成相应的指针类型,才能进行解引用操作。空类型指针不能进行解引用操作;不能进行±整数运算。同时在c语言里,空指针NULL指的是地址为0的那块空间,故对NULL是不能进行解引用操作的(对应错误类型3.1)

  • size_t是指无符号的整型
  • size是指大小,单位为字节
  • 如果开辟成功,则返回申请的空间的起始地址;如果开辟失败,则返回一个NULL指针,故malloc的返回值一定要检查
  • 返回值类型是void,所以malloc函数并不知道开辟空间的类型,具体由使用者自己决定,
  • 如果参数size为0,malloc的行为的标准是未定义的,取决于编译器。

c语言还提供了另一个函数free,专门用来做动态内存的释放与回收,void free(void* ptr);(用来释放动态开辟的内存)

  • free的功能不是把开辟的空间清除了,也不是把指针清空了,而是取消了指针和所对应内存的指向关系。
  • 如果参数ptr指向的空间不是动态开辟的,那么free函数的行为是未定义的
  • 如果参数ptr是NULL函数,则函数什么事也不做。

malloc与free都声明在stdlib.h头文件中。

例:

int main()
{
        int arr[10] = { 0 };  //向内存申请了10*4=40个字节的空间
        void* p = malloc(50);  //50字节
}

分析:在上面的void* p = malloc(50); 中,向内存空间申请了50个字节的空间,malloc会返回一个地址,将这个地址存入void*的指针里面去。

特别的,如果要将malloc开辟的40个空间里面放入10个整形,需要强制类型转换;需要将malloc开辟的void*类型的指针强制类型转换为:int *类型的指针。

int* p = (int*)malloc(50);

为什么必须释放开辟的动态内存空间:malloc是一个函数,表明了堆空间说在程序运行起来之后,再在系统上申请的,空间只申请不释放,会造成内存泄露。

2.2calloc

c语言提供了一个函数叫calloc(用来动态内存开辟)void* calloc(size_t num,size_t size);

函数功能:为num个大小为size的元素开辟一块空间,并把空间的每个字节都初始化为0.

与malloc的区别:malloc没初始化,效率高;calloc做了初始化,效率低

2.3realloc

  1. realloc函数的出现让动态内存管理更加灵活
  2. realloc可以做到对动态开辟的内存大小的调整

void* realloc(void* ptr,size_t size);

ptr是要调整的内存地址;size是调整之后的新大小;返回值为调整之后的内存起始地址;

这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移到新的空间;

realloc在调整内存空间存在两种情况:

1,原有空间之后有足够大的空间

2,原有空间之后没有足够大的空间

3.常见错误

3.1对NULL指针进行解引用操作

int main()
{
            int* p = (int*)malloc(40);
            *p = 0;// 万一malloc失败了,p就被赋值为NULL
            free(p);
            p = NULL;
            return 0;
}

解决方法:在申请空间后对p进行判断

if(p==NULL)

return 1;

3.2对动态开辟内存的越界访问

#include
int main()
{
             int *p = (int*)malloc(10*sizeof(int));//申请10个int空间大小
             if(p==NULL)
                          return 1;

             int i=0;
              // 越界访问
             for(i=0;i<40;i++)
             {
                    *(p+i)=i;
              }
}

3.3使用free释放非动态开辟的空间

int main()
{
           int arr[10]={0}//存于栈区
           int *p=arr;

             free(p);//使用free释放了数组申请的空间
             p=NULL;
             return 0;
}

3.4使用free释放了动态开辟内存的一部分

int main()
{
              int* p=(int*)malloc(10*sizeof(int));
              if(p==NULL)
                    return 1;

              int i=0;
                for(i=0;i<5;i++)
             {
                  *p++ = i;
             }
             free(p);
             p=NULL;
             return 0;
}

由于不当操作使得原本指向动态内存空间的p的指向发生改变,不能读取到完整的空间,如果使用free就会造成错误

注:p指向开辟内存的首地址,而在循环赋值中,p只对其中5个int大小进行了赋值,而后就对该空间释放。除此之外p受++运算符不断向后移动,使得p的初始位置改变,这很容易导致内存泄漏。

3.5对同一块动态内存开辟的空间多次释放·

int main()
{
int* p=(int*)malloc(100);
//使用
//释放
free(p);
p=NULL;

//释放
free(p);
return 0;
}

由于记忆问题导致在对一块动态内存空间释放后再次释放,造成错误

3.6动态开辟的空间忘记释放,造成内存泄漏

由于不断申请动态内存空间而忘记回收,造成每隔一段时间内存满了程序卡死,就是内存泄漏

解决方法:在该指向这块空间的指针的生命周期结束前使用free函数释放

4.参考

C/C++动态内存开辟详解(含常见错误以及经典面试题)_利刃Cc的博客-CSDN博客_c++开辟内存

内存管理---malloc/free和new/delete的区别_Exy-的博客-CSDN博客_malloc/free和new/delete的区别

5.与c++的差别(malloc/free和new/delete的异同点)

malloc/free和new/delete的共同点是:

都是从堆上申请空间,并且需要用户手动释放。

malloc/free和new/delete不同点是:

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不会初始化,new可以初始化
  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可
  4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间 后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

你可能感兴趣的:(数据结构,算法,c++,c语言,windows)