动态内存开辟(上)

简介: 动态内存开辟(上)

一. 为什么会存在动态内存分配


我们已经掌握了一下的内存开辟方式


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


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


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

2 数组在声明的时候 必须指定数组的长度 它所需要内存在编译时分配


特点1很好理解


就像上面 我们指定数组arr之后 在栈区上就不可以改变数组的大小了


但是我们使用malloc指定内存的大小之后 还是可以使用realloc来重置大小


特点2 我们使用一段代码来解释下

0ab6a14c057a4bb2bee4a0affdfdf771.png


我们发现 动态开辟内存可以不指定数组的长度 而是传递进去一个变量


有时候对于空间大小的需求 我们只有在程序运行的时候才能知道


这个时候我们就只能使用动态内存开辟了


二. 动态内存函数的介绍


1. malloc和free


1.1 malloc


c语言提供了一个动态内存开辟的函数:


void* malloc (size_t size);


这个函数像内存申请一块连续可用的空间 并且返回指向这块空间的指针 是一个无符号类型的指针


这其中有四个注意点


1 如果开辟成功,则返回一个指向开辟好空间的指针。

2 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。

3 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己

来决定。

4 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。


特点一说明了这个函数的返回值


特点二说明了它开辟失败的情况


特点三详细介绍了它返回值的类型


特点四说明了如果开辟的空间为0 这个时候malloc会做什么


这里c语言提供了另一个函数 专门史用来做动态内存的释放和回收的


void free (void* ptr);
• 1


1.2 free


free函数也有两个特点


1 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。

2 如果参数 ptr 是NULL指针,则函数什么事都不做。


这里特别要注意特点1 我们来看一段代码


int main()
{
  int a = 10;
  int* p = &a;
  free(p);
  p = NULL;
  return 0;
}


如果我们写出来上面这一段代码 那么我们的编译器会给我们报错


c79259d441fd4ccab2ef5bc138b94ea2.png


特点二是一个知识点 我们记一下就好了


指针指向NULL的时候 free什么都不做


int main()
{
  int a = 10;
  int* p = &a;
  p = NULL;
  free(p);
  return 0;
}


a2a47cc6747b41eea2c7bf42288029ed.png

1.3 完整的演示代码

int main()
{
  // 代码1
  int num = 0;
  scanf("%d", &num);
  int arr[50] = { 0 };
  //代码2
  int* p = (int*)malloc(num * sizeof(int));
  if (NULL == p)
  {
    perror(malloc);
  }
  else
  {
    int i = 0;
    for ( i = 0; i < num; i++)
    {
      *(p + i) = i;
    }
  }
  free(p);
  p = NULL;
  return 0;
}


2. calloc


c语言还提供了一个函数叫做 calloc calloc函数也用来动态内存分配


原型如下


void* calloc (size_t num, size_t size);


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

与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。


int main()
{
  int* p = (int*)calloc(40, sizeof(int));
  if (NULL==p)
  {
    perror(calloc);
  }
  free(p);
  p = NULL;
  return 0;
}


代码如上


我们来看看它们的内存


8d4c04ea6c1949e9ade937a866444980.png


我们可以发现全部初始化内存确实为0


3. realloc


1 realloc函数的出现让动态内存管理更加灵活。

2 有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时

候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小

的调整。


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


ptr 是要调整的内存地址

size 调整之后新大小

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

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


7f2a5c2e64e74351a76bfdd4248f133e.png


情况一 如果说原有空间之后又足够大的空间 那么就直接扩展内存追加空间 原理啊空间的数据不发生变化


情况二 当情况2的时候 原有空间之后没有足够多的空间 扩展的方法是 在堆空间上另外寻找一个合适大小的连续空间来使用 这样函数返回的是一个新的内存地址


int main()
{
  int* p = (int*)malloc(100);
  if (p==NULL)
  {
    perror(malloc);
  }
  else
  {
    ;
  }
  // p = (int*)realloc(p, 1000);
  // 这个写法因为有可能 realloc开辟内存失败 这样的话返回一个空指针
  // 开辟的内存空间就消失了 
  // 所以说 我们赋值前要判断是否为空指针
  int* ptr = (int*)realloc(p, 1000);
  if (ptr==NULL)
  {
    perror(realloc);
  }
  else
  {
    p = ptr;
  }
  free(p);
  p = NULL;
  return 0;
}


三. 常见的动态内存错误


3.1 对NULL指针的解引用操作


void test()
{
  int* p = (int*)malloc(INT_MAX / 4);
  *p = 20;//如果p的值是NULL,就会有问题 
  free(p);
}


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


int main()
{
  int* p = (int*)malloc(10);
  if (p==NULL)
  {
    perror(malloc);
  }
  else
  {
    int i;
    for ( i = 0; i <= 10; i++)
    {
      *(p + i) = i;
    }
  }
  free(p);
  p = NULL;
}


8db16ad542c64c05835dd5cd3683fb08.png


3.3 对于非动态开辟内存使用free释放


int main()
{
  int a = 10;
  int* p = &a;
  free(p);
}


fc328e17c4994bd18c915e47c7ce460b.png


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


int main()
{
  int* p = (int*)malloc(40);
  p++;
  free(p);
  return 0;
}

831c2c73124b4ddeb9b8fa95a812a0dd.png


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


int main()
{
  int* p = (int*)malloc(40);
  free(p);
  free(p);
  return 0;
}


a84415784dd543b48cabc042731d6d14.png


3.6 动态开辟内存忘记释放(内存泄漏)


int main()
{
  while (1)
  {
    int* p = (int*)malloc(20);
    if (NULL == p)
    {
      perror(malloc);
    }
    else
    {
      *p = 20;
    }
  }
  return 0;
}


以上就是本篇博客的全部内容啦 由于博主才疏学浅 所以难免会出现纰漏 希望大佬们看到错误之后能够


不吝赐教 在评论区或者私信指正 博主一定及时修正


那么大家下期再见咯



相关文章
|
8月前
|
编译器 C语言 C++
【C语言】realloc()函数详解(动态内存开辟函数)
【C语言】realloc()函数详解(动态内存开辟函数)
115 0
|
8月前
|
编译器 C++
C/C++动态内存开辟(详解)
C/C++动态内存开辟(详解)
|
存储 编译器 C语言
C语言进阶第六课-----------字符分类函数和内存的开辟 2
C语言进阶第六课-----------字符分类函数和内存的开辟
|
C语言
C语言进阶第六课-----------字符分类函数和内存的开辟 1
C语言进阶第六课-----------字符分类函数和内存的开辟
|
程序员 编译器 C语言
动态内存函数,内存开辟,柔性数组(超详细)
动态内存函数,内存开辟,柔性数组(超详细)
88 0
|
Unix 程序员 Linux
【OSTEP】动态内存开辟 | 内存API常见错误 | UNIX: brk/sbrk 系统调用 | mmap创建匿名映射区域 | mmap创建以文件为基础的映射区域
【OSTEP】动态内存开辟 | 内存API常见错误 | UNIX: brk/sbrk 系统调用 | mmap创建匿名映射区域 | mmap创建以文件为基础的映射区域
275 0
|
7月前
|
C语言
C语言学习记录——动态内存开辟常见的错误
C语言学习记录——动态内存开辟常见的错误
41 1
|
8月前
|
编译器 C++
内存对齐与内存开辟。结构体(struct),位段,枚举类型(enum),联合体(union)。
内存对齐与内存开辟。结构体(struct),位段,枚举类型(enum),联合体(union)
47 1
|
7月前
|
C语言
动态内存开辟(下)
动态内存开辟(下)
32 0
|
7月前
|
编译器 C语言
动态内存开辟(上)
动态内存开辟(上)
33 0