动态内存

我们平时内存开辟的方式:

int val=20;//在栈空间开辟四个字节
char arr[10]={0};//在栈空间上开辟10个字节的连续空间

这样开辟空间的方式有两个特点:

  • 空间开辟的大小是固定的
  • 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组编译时开辟空间的方式就不能满足了。
这时候就需要动态内存开辟

malloc & free

  • 定义:全称是memory allocation,中文叫动态内存分配,用于申请一块连续的指定大小的内存块区域,以void*类型返回分配的内存区域地址。
    一般需和free函数配对使用,free函数能释放某个动态分配的地址,表明不再使用这块动态分配的内存了,实现把之前动态申请的内存返还给系统。

  • 构造

void *malloc (size_t size);
void free(void *ptr);
  • 使用
    • 如果开辟成功,则返回一个指向开辟好空间的指针
    • 如果开辟失败,则返回一个NULL指针,因此malloc的函数值一定要做检查
    • 返回值的类型是void *,所以malloc的行为是标准未定义的,取决于编译器
#include <stdio.h>
int main()
{
    int *ptr=NULL;
    ptr=(int *)malloc(10*sizeof(int));//开辟40个字节
    if(NULL!=ptr)//使用malloc动态开辟内存,首先进行判断是否成功开辟
    {
        int i=0;
        for(i=0;i<10;i++)
        {
            *(ptr+i)=i;
        }
    }
    free(ptr);//堆上的数据,使用后要释放,否则,会造成内存泄漏
    ptr=NULL;//free不能把指针置空,代码执行完所以要将指针置空
    return 0;
}

calloc

  • 定义:在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。
  • 构造
    void *calloc(size_t n, size_t size);
  • 使用
#include <stdio.h>
#include <stlid.h>
int main()
{
    int *p=(int *)calloc(10,sizeof(int));
    if(NULL != p)
    {
        ;
    }
    free(p);
    p=NULL;
    return 0;
}

realloc

  • 定义:对动态内存的大小进行调整
  • 构造
    void *realloc(void *mem_address, unsigned int newsize);

    • mem_address是要调整的地址
    • newsize调整之后新大小
    • 返回值为调整之后的内存起始位置
  • 功能
    • 先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回;
    • 如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域
    • 注意:原指针是自动释放,不需要使用free,同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。
#include <stdio.h>
#include <stlid.h>
int main()
{
    int *ptr = NULL;
    ptr = (int *)malloc(10* sizeof(int));
    if (NULL != ptr)
    {   
        //业务处理
    }
    int *p = (int *)realloc(ptr,50);
    if (NULL != p)
        ptr = p;
    else
    {
        printf("out of memory!");
        return 0;
    }

        //业务处理

    free(ptr);
    ptr = NULL;
    system("pause");
    return 0;
}
  • 使用

    1. realloc失败的时候,返回NULL

    2. realloc失败的时候,原来的内存不改变,不会释放也不会移动

    3. 假如原来的内存后面还有足够多剩余内存的话,realloc的内存=原来的内存+剩余内存,realloc还是返回原来内存的地址; 假如原来的内存后面没有足够多剩余内存的话,realloc将申请新的内存,然后把原来的内存数据拷贝到新内存里,原来的内存将被free掉,realloc返回新内存的地址

    4. 如果size为0,效果等同于free()。这里需要注意的是只对指针本身进行释放,例如对二维指针**a,对a调用realloc时只会释放一维,使用时谨防内存泄露。

    5. 传递给realloc的指针必须是先前通过malloc(), calloc(), 或realloc()分配的

    6. 传递给realloc的指针可以为空,等同于malloc。

常见的动态内存错误

  • 对NULL指针的解引用操作
 void test() 
{    
    int *p = (int *)malloc(INT_MAX/4);    
    *p = 20;// 如果 p 的值是 NULL ,就会有问题    
    free(p); 
}

改正: 需要if(NULL!=p)判断,若开辟失败,返回空指针

  • 对动态开辟空间的越界访问
void test() 
{    
    int i = 0;  
    int *p = (int *)malloc(10*sizeof(int));    
    if(NULL == p)    
    {        
        exit(EXIT_FAILURE);    
    }    
    for(i=0; i<=10; i++)    
    {        
        *(p+i) = i;// 当 i 是 10 的时候越界访问    
    }    
    free(p); 
}

改正:循环 for(i=0;i<10;i++)左开右闭!

  • 对非动态开辟内存使用free释放
void test() 
{    
    int a = 10;
    int *p = &a;    
    free(p);//p是指针变量,不是动态开辟
}

改正:将p进行动态内存开辟使用
int main()
{
int a = 10;
int *p = (int *)malloc(sizeof(int));
*p = 20;
free(p);
p = NULL;
system("pause");
return 0;
}

- 对同一块动态内存多次释放

void test()
{
    int *p=(int *)malloc(100);
    free(p);
    free(p);//重复释放
}

改正:对空指针再次释放没有问题
free(p);
p=NULL;
free(p);

  • 动态开辟内存忘记释放(内存泄漏)
void test()
{
    int *p=(int *)malloc(100);
    if(NULL != p)
        *p=20;//没有free()释放内存
}
int main()
{
    test();
    while(1);
}   

改正:
void test()
{
int *p=(int *)malloc(100);
if(NULL != p)
*p=20;
free(p);
}

总结

  • 当数组被声明时,必须在编译时知道它的长度。动态内存配允许程序为一个长度在一个长度运行时才知道的数组分配内存空间。
  • malloc和calloc函数都是用于动态分配一块内存,并返回一个指向该内存的指针。malloc的参数就是需要分配的内存的字节数。callocd的参数是你需要分配的元素个数和每个元素的长度。calloc函数在返回前把内存初始化为0,而malloc函数返回时内存并未以任何方式进行初始化。
  • realloc函数可以改变一块已经动态分配的内存的大小。增加内存的块大小时有可能采取的方法是把原来内存块上的所有数据复制到一个新的、更大的内存块上。
  • 当一个动态分配的内存块不在使用时,应该调用free函数把它归还给可用的内存池。内存释放后便不能被访问。

猜你喜欢

转载自blog.csdn.net/sifanchao/article/details/80247647