内存分配者-动态内存

简介: 动态内存

文章目录


动态内存管理

为什么存在动态内存分配

我们到现在为止掌握的是什么样的内存开辟方式呢

//创建一个变量
int val = 20;    //局部变量  在栈空间中开辟4个字节
int g_val = 10;  //全局变量  在静态区中开辟4个字节
//创建一个数组
char arr[10] = {0}; //局部区域 在栈空间中开辟10个字节连续的空间
char g_arr[5] = {0};//全局区域 在静态区空间中开辟5个字节的连续空间

image.png

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


  1. 空间开辟大小是固定的。
  2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
    但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。 这时候就只能试试动态内存开辟了。

c99是支持变长数组的,但现在很多编译器就不支持c99,连vs都不支持,所以就有动态内存的概念


动态内存函数的介绍

malloc申请空间和free释放空间

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

void* malloc(size_t size);=

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

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

image.png

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

image.png

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

image.png

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

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
  //向内存申请10个整形的空间
  int* p = (int*)malloc(10 * sizeof(int));
  if (p == NULL)
  {
    //把开辟失败的信息打印出来
    printf("%s",strerror(errno));
  }
  else
  {
    //正常使用空间
    int i = 0;
    for (i = 0; i < 10; i++)
    {
      *(p + i) = i;//在找下标为i的元素
    }
    for (i = 0; i < 10; i++)//再把每个元素打印出来
    {
      printf("%d ", *(p + i));
    }
  }
  return 0;
}

image.png

那我们可不可以看开辟失败的呢

我们可以用INT_MAX(他是整形最大),一个超级大的数字


image.png

image.png


有借有还 free释放内存


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

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

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

image.png

image.png

注意

malloc和free是成对使用的,谁开辟谁释放


calloc申请内存

在内存中开辟一个数组,把元素都改成零

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

image.png

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

image.png


realloc调整动态内存的大小

当然我们可以申请空间,但会不会遇到申请的空间不够了,想要增加一些些,大了想要去掉一些些

image.png


realloc使用的注意事项

1.如果p指向的空间之后有足够的内存空间可以追加,则直接追加,后返回p

2.如果p指向的空间之后没有足够的内存空间可以追加,则realloc函数会重新找一块新的内存区域,开辟一块满足需求的空间,并且把原来的内存中的数据拷贝回来,释放旧的内存空间,最后返回新开辟的内存空间地址

3.但也有一个大问题,就是开辟INT_MAX,用新的变量ptr来接收realloc返回值

image.png

image.png


当然realloc也可以直接开辟空间

image.png


常见的动态内存错误

1.对NULL指针的解引用操作

#include<stdio.h>
#include<stdlib.h>
int main()
{
  int* p = (int*)malloc(40);//没成功就会有大问题
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    *(p + i) = i;
  }
  free(p);
  p = NULL;
  return 0;
}

image.png

所以为了防止没有开辟动态内存成功就需要做个判断

image.png

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

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
  int* p = (int*)malloc(5*sizeof(int));
  if (p == NULL)//这里我的确判断有没有开辟成功了
  {
    printf("%s", strerror(errno));
  }
  else
  {
    int i = 0;
    for (i = 0; i < 10; i++)//但是我这里访问10个整型的空间
    {
      *(p + i) = i;
    }
  } 
  free(p);
  p = NULL;
  return 0;
}

image.png


3.对非动态开辟内存使用free释放

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

image.png

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

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
  int* p = (int*)malloc(40);
  if (p == NULL)
  {
    return 0;//如果是空指针就直接返回,不干了
  }
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    *p++ = i;//这个++就是bug的地方
  }
  //回收空间
  free(p);
  p = NULL;
  return 0;
}

image.png

只要p不是指向申请的空间的首地址,其他地方都是错的


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

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
  int* p = (int*)malloc(40);
  if (p == NULL)
  {
    return 0;
  }
  //使用
  //释放
  free(p);
  //...
  free(p);
  return 0;
}

image.png


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

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
  while (1)
  {
    malloc(100);
  }
  return 0;
}

image.png

几个面试题

题目1

void GetMemory(char* p)
{
  p = (char*)malloc(100);
}
void Test(void)
{
  char* str = NULL;
  GetMemory(str);
  strcpy(str,"hello world");
  printf(str);//这个写法和printf("%s",str);是一样的
}
int main()
{
  Test();
  return 0;
}

问运行Test函数会有什么样的结果

image.png

修改正确

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void GetMemory(char* *p)
{
  *p = (char*)malloc(100);
}
void Test(void)
{
  char* str = NULL;
  GetMemory(&str);
  strcpy(str,"hello world");  
  printf(str);//这个写法和printf("%s",str);是一样的
  free(str);//用完就释放
  str = NULL;
}
int main()
{
  Test();
  return 0;
}

image.png

题目2

char* GetMemory(void)
{
  char p[] = "hello world";
  return p;
}
void Test(void)
{
  char* str = NULL;
  str = GetMemory();
  printf(str);
}
int main()
{
  Test();
  return 0;
}

请问运行Test 函数会有什么样的结果

输出随机值

image.png

image.png

修改正确

既然是p被销毁了,那我们让他不销毁就可以了延长它的生命周期用static

char* GetMemory(void)
{
  static char p[] = "hello world";
  return p;
}
void Test(void)
{
  char* str = NULL;
  str = GetMemory();
  printf(str);
}
int main()
{
  Test();
  return 0;
}

image.png

题目3

void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}

这题基本和第一题一样,不过这题就只有内存泄漏的错误

image.png

修改正确

#include<stdio.h>
#include<stdlib.h>
void GetMemory(char** p, int num)
{
  *p = (char*)malloc(num);
}
void Test(void)
{
  char* str = NULL;
  GetMemory(&str, 100);
  strcpy(str, "hello");
  printf(str);
  free(str);//用完就释放,防止内存泄漏
  str = NULL;
}
int main()
{
  Test();
  return 0;
}

image.png


题目4

void Test(void)
{
  char* str = (char*)malloc(100);
  strcpy(str, "hello");
  free(str);
  if (str != NULL)
  {
    strcpy(str, "world");
    printf(str);
  }
}

问题非常大的打印出结果

image.png

image.png


修改正确

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void Test(void)
{
  char* str = (char*)malloc(100);
  strcpy(str, "hello");
  free(str);//这里考查的是free释放后并没有使str为NULL,所以下面if判断就没有作用,如果使他有作用就让str为NULL
  str = NULL;
  if (str != NULL)
  {
    strcpy(str, "world");
    printf(str);
  }
}
int main()
{
  Test();
  return 0;
}

这道题真正目的就是让你什么都不打印

image.png

C/C++程序的内存开辟

image.png


C/C++程序内存分配的几个区域:

  1. 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函
  2. 数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
  3. 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分配方式类似于链表。
  4. 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
  5. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。

目录
相关文章
|
1月前
|
程序员 C语言
动态内存分配
【10月更文挑战第10天】
41 5
|
1月前
|
程序员 C语言 开发者
内存分配
【10月更文挑战第9天】
66 8
|
5月前
|
C语言
动态内存
【6月更文挑战第16天】
38 10
|
6月前
|
安全 C++ 容器
C++ 动态内存
C++ 动态内存
35 0
|
6月前
|
Kubernetes 网络性能优化 调度
k8s的内存分配
k8s的内存分配
|
存储 程序员 编译器
静态内存与动态内存 & malloc函数动态分配内存
静态内存与动态内存 & malloc函数动态分配内存
149 0
|
存储 安全 程序员
内存分配理解
内存分配理解
151 0
|
存储 C语言 C++
【C++初阶】C++内存分配与动态内存管理
【C++初阶】C++内存分配与动态内存管理
|
C语言 C++
C++中的动态内存分配
C++中的动态内存分配
130 0
|
存储 Linux C语言
C++——内存分配与动态内存管理
C++——内存分配与动态内存管理
76 0