解密动态内存管理的奥秘(含内存4个函数)

简介: 解密动态内存管理的奥秘(含内存4个函数)

187bb465cf644158aa6c3e06b6ba93f9.jpg


一.为什么存在动态内存管理



我们常见的内存开辟方式:

int  a =  20;               //在栈空间上开辟四个字节

int  arr[10] = { 0 };     //在栈空间上开辟40个字节


但是上的开辟空间方式有两个特点:

1.开辟的空间的大小是固定的;

2.数组在申明的时候,必须指定固定的长度,它所需的内存在编译时分配,有可能用不完造成浪费,也有可能后期不够用;


但是下在我们实际所需求中,不仅仅是上面的两种情况,有时候我们需要的空间大小在程序运行时才知道,那么上述方式开辟空间就不能够满足,所以有了动态内存开辟。


二.动态内存函数的介绍



必备知识:


内存大概的划分:

栈区: 主要存放局部变量,形式参数等等

堆区: 动态内存的开辟,malloc,free,calloc,realloc等等

静态区: 全局变量,静态变量等等

37ea2d54507a4e1291c08ac8211c3100.png


1. malloc函数(memory  alloc  内存开辟)


函数介绍:


void*   malloc(size_t    size)

malloc函数可以向内存申请一块连续的空间,并返回指向这块空间的指针;

值得注意的是:

1. 如果开辟成功,将返回一个指向开辟好了的空间的指针;

如果开辟失败,将返回空指针NULL;

所以在开辟后需要判断是否开辟成功,检查返回值;

2. 函数返回的是空指针,malloc函数并不知道开辟空间的类型,所以在使用的时候需要自己决定(使用强制类型转换);

3.如果size为0时,malloc函数的行为是未定义的,取决于编译器;

4.malloc函数开辟的空间里面存放的是随机值(下面代码可验证);

5.malloc函数申请的空间,当程序退出时,还给操作系统,当程序不退出时,动态开辟的空间不会主动还给操作系统,需要用free函数来释放;


malloc函数使用举例代码:

int main()
{
  int* p = (int *)malloc(40);   //这里需要的是int*类型的指针,所以用()强制类型转换为int*
  if (p == NULL)
  {
    perror(malloc);           //判断是否开辟成功
    return 1;
  }
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d\n",*(p+i));
  }
  free(p);
    p=NULL;         //将p置为空指针,不要让p成为野指针
  return 0;
}


运行结果:

-842150451
-842150451
-842150451
-842150451

-842150451
-842150451
-842150451
-842150451
-842150451
-842150451


2.free(释放)


函数介绍:


void*   free (void*  p)

C语言提供的free函数是专门用来做动态内存的释放和回收的

free是用来释放动态开辟的内存

1.如果void* p所指向的内存不是动态开辟的,free函数的行为是未定义的;

2.如果void* p所指向的是NULL(空指针),则free函数什么都不做;


代码的示例:


int main()
{
  int* p = (int *)malloc(40);   //这里需要的是int*类型的指针,所以用()强制类型转换为int*
  if (p == NULL)
  {
    perror(malloc);           //判断是否开辟成功
    return 1;
  }
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d\n",*(p+i));
  }
  free(p);       //使用完该空间后释放
    p=NULL;         //将p置为空指针,不要让p成为野指针
  return 0;
}


3.calloc


函数介绍:


void*   calloc (size_t  num , size_t   size)

功能:是为num个大小为size的元素开辟一块空间,并吃初始化为0;

与malloc函数类似,只是会将开辟的空间内容初始化为0;


代码示例:


int main()
{
    int* p = (int* )calloc(40,sizeof(int));
  if (p == NULL)
  {
    perror(calloc);
    return 1;
  }
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ",*(p+i));
  }
  free(p);
  p = NULL;
  return 0;
}


运行结果:


0   0   0   0   0   0   0   0   0   0


4.realloc


realloc函数可以让内存管理更加灵活;


函数介绍:


viod*   realloc    (void*   ptr,size_t   size)

realloc函数可以调整我们申请的空间的大小;

其中ptr是要调整的内存地址;

size是调整之后的大小;

返回值是为调整之后的内存起始位置;

当void*  ptr  为一个空指针时,功能和malloc一样;

值得注意的是:这个函数调整之后,可能将原来内存中的数据移动到新的空间;


分为两种情况;

1.原有空间的后面有足够大的空间时(即原有空间后面的剩余空间有所需增加空间那么大),就直接在原有空间后面开辟,并且返回指向原来空间的指针;

2.当原有的空间后面没有足够大的空间来增加空间,则需要重新找一个空间开辟,并且将原有空间的内容复制过去,然后释放旧的空间的内存,返回新的地址;


使用示例代码:

int main()
{
  int* p =(int* )malloc(40);
  if (p == NULL)
  {
    perror("malloc");
    return 1;
  }
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    p[i] = i+1;
  }
  int* ret = realloc(p,80);
  {
    if (ret == NULL)
    {
      perror("realloc");
      return 1;
    }
    else
    {
      p = ret;
    }
  }
  for (i = 0; i < 20; i++)
  {
    printf("%d ",*(p+i));
  }
  free(p);
  p = NULL;
  return 0;
}


运行结果:

1  2  3  4  5  6  7  8  9  10  -842150451  -842150451  -842150451  -842150451  -842150451  -842150451  -842150451  -842150451 -842150451 -842150451


常见的动态内存管理的错误



1.对NULL指针进行解引用操作


    void   test

    {  

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

            *p = 20;    //如果p是空指针就有问题;

             free(p);

    }

当用内存函数开辟好空间后,不检查是否开辟成功就直接使用;


2.对动态开辟的空间越界访问


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

int i = 0;

for(i=0;i<12;i++)                 //  i=10,11,就越界访问了

{

    printf("%d ",*(p+i));

}


3.对非动态开辟的空间使用free


int a = 10;

int* p = &a;

free(p);           //  错误的


4.使用free释放一块动态开辟的内存中的一部分


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

p++;

free(p);          //p++后,已经不是指向的原开辟的内存,而是指向的一部分;


5.对同一块内存多次释放


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

free(p);

free(p);


6.动态内存开辟忘记释放,造成内存泄漏

void   test()

{

        int * p =(int*)mmalloc(40)

         if(NULL!=p)

        {

                *p=20;

         }

}                                                      //使用完后忘记释放,出函数p销毁,但是40个字节的空间还在

int  main()

{

        test();

        while(1);                              //死循环,程序不结束,40个字节永远用不到

}

目录
相关文章
|
2月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
41 3
|
20天前
|
存储 监控 算法
Java内存管理深度剖析:从垃圾收集到内存泄漏的全面指南####
本文深入探讨了Java虚拟机(JVM)中的内存管理机制,特别是垃圾收集(GC)的工作原理及其调优策略。不同于传统的摘要概述,本文将通过实际案例分析,揭示内存泄漏的根源与预防措施,为开发者提供实战中的优化建议,旨在帮助读者构建高效、稳定的Java应用。 ####
34 8
|
21天前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
49 6
|
1月前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
49 6
|
2月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
2月前
|
存储 程序员 编译器
C语言——动态内存管理与内存操作函数
C语言——动态内存管理与内存操作函数
|
2月前
|
存储 缓存 监控
深入了解MySQL内存管理:如何查看MySQL使用的内存
深入了解MySQL内存管理:如何查看MySQL使用的内存
436 1
|
2月前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
40 0
|
2月前
|
C语言 C++
c语言回顾-内存操作函数
c语言回顾-内存操作函数
48 0
|
2月前
|
存储 C语言 C++
来不及哀悼了,接下来上场的是C语言内存函数memcpy,memmove,memset,memcmp
本文详细介绍了C语言中的四个内存操作函数:memcpy用于无重叠复制,memmove处理重叠内存,memset用于填充特定值,memcmp用于内存区域比较。通过实例展示了它们的用法和注意事项。
78 0