【C语言航路】第十三站:动态内存管理(上)

简介: 【C语言航路】第十三站:动态内存管理

一、为什么存在动态内存分配

我们已经知道的内存开辟方式有

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

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

但是这样开辟出来的空间有两个特点

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

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

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

二、动态内存函数

 

1.内存的分区

我们首先要知道,我们的内存大致可以分为栈区,堆区,静态区的

我们现在所说的动态内存函数都是在堆区的。

2.malloc和free

(1)malloc和free库函数文档

从这里我们可以得知,我们这里接受一个整型变量size,开辟一个size字节的空间大小。这块空间是连续的。然后返回这块空间的起始地址,这个返回类型是void*,所以我们开辟好后需要进行强制类型转化

这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。

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

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

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

来决定。

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

free这个函数是用于释放我们动态开辟的内存空间。因为动态开辟的空间都是在堆区的。和局部变量这些开辟在栈区不同的是。他们会有栈帧的销毁的。所以不用我们去回收这块空间。而堆区的空间只有靠free来销毁回收。虽然说当程序结束的时候堆区的内存也会被销毁掉,但是如果程序很长时间不关闭的话,就很容易出现内存泄漏

free函数用来释放动态开辟的内存。

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

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

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

(2)malloc和free的使用

这就是我们malloc的使用了,要注意我们一定要检查malloc出来的指针是否为空,因为有可能申请失败了。就会返回空指针,而我们后续一旦解引用空指针就会出现大问题

这里我们也使用了free这个函数,这个函数的功能是释放掉使用的堆区的内存。我们这个程序没有free固然是没有任何问题的。因为程序结束后也会释放这个内存。但是如果对于有循环不会结束的程序,那么问题就严重了,会造成内存泄漏,而我们free后也要记得将p置空,否则p就是野指针

#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
int main()
{
  int* p = (int*)malloc(40);
  if (p == NULL)
  {
    printf("%s", strerror(errno));
    return 1;
  }
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    *(p + i) = i;
  }
  for (i = 0; i < 10; i++)
  {
    printf("%d ", *(p + i));
  }
  free(p);
  p = NULL;
  return 0;
}

当然我们也可以演示一下内存开辟失败的情况

2.calloc

(1)calloc的库函数文档

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

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

(2)calloc的使用

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
  int* p = (int*)calloc(10, 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;
}

(3)与malloc的区别

malloc申请到的空间没有初始化,直接返回起始地址

calloc申请到的空间,会将所有元素初始化为0,然后返回起始地址

当然由于calooc需要初始化,所以calloc的效率会更慢一些

3.realloc

(1)realloc的库函数文档

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

有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整

ptr 是要调整的内存地址,他时被malloc、calloc、realloc出来的

size 调整之后新大小

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

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

realloc在调整内存空间的是存在几种情况:

情况1:原有空间之后有足够大的空间

在这种情况下,relloc就会在原来的地址后面续上新的空间,返回旧的起始地址

情况2:原有空间之后没有足够大的空间
在这种情况下,realloc就会重新找一块满足空间大小的新的连续空间,把旧的空间的数据,拷贝到新的空间中去,然后释放旧的空间。同时返回新的空间的地址

情况3:realloc开辟失败

直接返回空指针,但是不会销毁旧的空间数据

情况4:realloc传一个空指针

此时就相当于一个malloc

(2)realloc的使用

#include<stdio.h>
#include<stdlib.h>
int main()
{
  int* ptr = (int*)malloc(20);
  if (ptr == NULL)
  {
    perror("malloc");
    return 1;
  }
  int i = 0;
  for (i = 0; i < 5; i++)
  {
    *(ptr + i) = i + 1;
  }
  int* p = (int*)realloc(ptr, 40);
  if (p != NULL)
  {
    ptr = p;
    p == NULL;
  }
  for (i = 0; i < 10; i++)
  {
    printf("%d ", *(ptr + i));
  }
  free(ptr);
  ptr = NULL;
  return 0;
}

三、常见的动态内存错误

1.对NULL指针的解引用

如下代码所示,我们使用malloc时候,未进行判断指针p是否为NULL,如果没有判断且指针为空,那么则会程序崩溃

#include<stdlib.h>
int main()
{
  int* p = (int*)malloc(INT_MAX);
  *p = 20;//如果p的值是NULL,就会有问题
  free(p);
  return 0;
}

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

如下代码所示,我们开辟了100个字节,也就是25个整型,但是我们访问了100个整型,这样就越界访问了,程序会崩溃

#include<stdio.h>
#include<stdlib.h>
int main()
{
  int* p = (int*)malloc(100);
  if (p == NULL)
  {
    perror("malloc");
    return 1;
  }
  int i = 0;
  for (i = 0; i < 100; i++)
  {
    *(p + i) = i + 1;
  }
  free(p);
  p = NULL;
  return 0;
}
相关文章
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
33 3
|
20天前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
32 3
|
1月前
|
编译器 程序员 C语言
深入C语言:动态内存管理魔法
深入C语言:动态内存管理魔法
|
1月前
|
存储 程序员 编译器
C语言——动态内存管理与内存操作函数
C语言——动态内存管理与内存操作函数
|
1月前
|
程序员 C语言
C语言内存函数精讲
C语言内存函数精讲
|
22天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
19 0
|
1月前
|
C语言
保姆级教学 - C语言 之 动态内存管理
保姆级教学 - C语言 之 动态内存管理
18 0
|
1月前
|
存储 C语言
深入C语言内存:数据在内存中的存储
深入C语言内存:数据在内存中的存储
|
1月前
|
C语言 C++
c语言回顾-内存操作函数
c语言回顾-内存操作函数
39 0
|
1月前
|
存储 C语言 C++
来不及哀悼了,接下来上场的是C语言内存函数memcpy,memmove,memset,memcmp
本文详细介绍了C语言中的四个内存操作函数:memcpy用于无重叠复制,memmove处理重叠内存,memset用于填充特定值,memcmp用于内存区域比较。通过实例展示了它们的用法和注意事项。
64 0