c语言学习第三十一课----动态内存开辟

简介: c语言学习第三十一课----动态内存开辟

动态内存分配

为什莫要动态内存分配?

已掌握的空间开辟都是静态的,无法实现需要多少,开辟多少.这里动态开辟能跟加满足我们的需求。

动态内存开辟的函数  malloc  calloc  realloc   free.

我们这里先说free,在动态内存扩容完成后,使用后就需要对该内存free.

free函数

2000a069944a43feb3814fc83e539fc8.png

free  释放空间,参数为空间的地址

函数类型为无返回的函数,参数为指针。功能是释放掉动态开辟的空间,因为动态开辟的空间都是在堆区开辟的,使用完后,应立马释放掉,并初始化指针为零,否则会形成野指针。而free函数就是专门用来释放掉开辟的空间。

8c599121b58f428c85475f53649b4084.png

功能,开辟内存块。在内存申请size_t大小的空间,并返回这个起始空间,类型为void *,储存在堆区中。

单位是字节。参数为size_t的大小类型。

int main()
{
  int *p=(int *)malloc(20);//申请20个字节的空间  这里的类型整形比较合适
  //空间申请失败(比如申请的太大)会返回一个空指针,且无法使用。
  //若参数为0,无大小,malloc的行为是未定义的,取决于编译器。
  if(p==NULL)
    {
    printf("%s\n", strerror(errno));
    return 1;
    }
  //使用
  //按照数组的形式访问使用
  for (int i = 0; i < 5; i++)
  {
    *(p + i) = i + 1;//存放整形数据0,1,2,3,4.
    //p[0]=0;p[1]=1;......
  }
  //malloc开辟的空间存放的是随机值
  //释放动态内存开辟的空间
  free(p);
  // free 后指针不会置空,此时p为一个野指针  还需要为空。
  p = NULL;
  return 0;
}

calloc 函数

e30044fb45e6497fbdf21951c9cc7541.png

也是开辟空间,开辟之后且用0来初始化,共有num个元素,元素大小为size_t,返回一个起始地址只想给内存块。参数为元素大小与元素空间大小,返回值为扩容后的地址空间。

int main()
{
  int* p = (int*)calloc(10, sizeof(int));
  return 0;
  //使用
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d", p[i]);//打印1 2 3 4 5 6 7 8 9 10
    return 1;
  }
  //释放
  free(p);
  p = NULL;
  return 0;
}

malloc与calloc区别  

1.参数不一样  

2.功能有所差异,都是在堆区上申请空间,但是malloc不初始化,calloc初始化,根据所需使用。

realloc函数

74cd948c17aa43fb88c5ddc6c6c2547d.png

realloc  开启的空间更加灵活,

参数为指针与扩充的空间大小  。函数返回类型为一个指向新扩容地址空间。函数功能是改变在指针的空间大小。

int main()
{
  int* p = (int*)mmalloc(20);
  if (p == NULL)
  {
    printf("%s\n", strerror(errno));
    return 1;
  }
  //使用
  int i = 0;
  for (i = 0; i < 5; i++)
  {
    p[i + 1] = i  +1;
  }
  //可能返回空指针
  //这里创建临时指针来存放
    //假设空间不够用
  int * ptr =(int *) realloc(p, 40);
  if (ptr != NULL)
  {
    p = ptr;
  }
  else
  {
    printf("realloc:%s\n", strerror(errno));//解释错误
  }
  //使用
  int i = 0;
  for (i=0; i < 10; i++)
  {
    p[i+1] = i + 1;
    for (int i = 0; i < 10; i++)
    {
      printf("%d \n", p[i]);
    }
  }
  //释放
  free(p);
  p = NULL;
  return 0;
}  

假设空间不够用

    开辟的空间可能是连续的,后接旧地址开辟空间。返回的是就指针。

    也有可能是另外开辟的

   若该地方不够,则会重开辟更大一块空间,把原来的数据重新拷贝到新空间  , 返回新空间的地址

使用完之后,释放旧的空间,如图表示:


3a34bdf27b2f4af6bf25605f724e7625.png

常见动态内存错误

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

int main()
{
  int* p = (int*)malloc(20);
  int i = 0;
  //可能会出现对NULL指针的操作
  //所以要对指针判断是否为空
  for (i = 0; i < 5; i++)
  {
    p[i] = i;//这里会报错
  }
  free(p);
  p = NULL;
  return 0;
}

若扩容失败返回的是一个空指针并且未判断,则后对其解引用操作会出问题。

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

int main()
{
  int* p = (int*)malloc(10);
  int i = 0;
  if(p == NULL)
{
    return 1;
}
  //越界访问   
  for (i = 0; i < 5; i++)
  {
    p[i] = i;
  }
  free(p);
  p = NULL;
  return 0;
}

这里开辟的空间不够足够储存,只有10个字节,却要存放五个整形数据,则这里就是越界访问。程序崩溃,未响应。

3.对非动态内存开辟的空间free  不再堆区

int main()
{
  int arr[10] = { 0 };
  int* p = arr;
  free(p);
  p = NULL;
  //程序崩溃
}

不在堆区中的空间,违法free.free作用的对象是动态开辟的内存空间。

4.使用free释放的空间的一部分

int main()
{
  int* p = (int*)malloc(40);
  int i = 0;
  for (i = 0; i < 5; i++)
  {
    //p[i] = i + 1;正确使用
    *p = i + 1;  //1 2 3 4 5 
    p++;
  }
  //释放
  free(p);//。这里只释放了前五个整形的空间后,后面的20个字节
  p = NULL;//程序崩溃
  return 0;
}

5.对同一块空间多次释放

int main()
{
  int* p = (int*)malloc(20);
  if (p == NULL)
  {
    return 1;
  }
  //使用之后
  free(p);
  //释放
  //若置空  则在此时程序不会崩溃   p=NULL;
  free(p);
  p = NULL;
}

这里注意。只要指针置空,则再次释放也可以。

6.动态内存忘记释放

void test()
{
  int* p = (int*)malloc(20);
  if (*p = NULL)
  {
    return;
  }
  //使用
  //假设用来存放了数据
}
int main()
{
  test();
  return 0;
}
//一直吃内存,但不释放。电脑就会卡死。

程序正常结束时,若没使用free释放,则会被程序回收

但没结束,也没释放,则会内存泄漏。

动态实现通讯录

上次是实现的静态通讯录,更改为动态实现,这里主要说几个变更的。

这里的结构创建,采用结构体指针指向成员的空间代替静态数组,增加表示空间容量的capacity

typedef struct  peoInfol
{
  char name[20];
  int age;
  char sex[5];
  char telep[12];
  char addr[30];
}peoInfo;
typedef struct contact
{
  peoInfo *data;//指向成员空间的指针
  int size;//有效个数
  int capacity;//空间大小能存放的个数
}Contact, * pContact;

新增宏定义

#define MAX 100
#define MAX_NAME 20
#define MAX_SIZE 5
#define MAX_ADDR 5
#define DEFAULT_SZ 3  //默认三个成员
#define INC_SZ  2 //每次扩容扩两个 

初始化函数

void Initcontact(Contact* pc)
{
  pc->data = (peoInfo*)malloc(DEFAULT_SZ * sizeof(peoInfo));
  if (pc->data == NULL)
  {
    printf("通讯录初始失败:%s\n",strerror(errno));
    return;
  }//初始化函数
  pc->size = 0;
  pc->capacity = 0;
}

扩容函数

void checkcapacity(Contact* pc,int sizeincrease)
{
  if(pc->size == pc->capacity)
  {
          peoInfo* ptr = (peoInfo*)realloc(pc->data,( pc->capacity+ INC_SZ) *  sizeof(peoInfo));
      if (ptr == NULL)
      {
        printf("扩容失败:%s",strerror(errno));
        return;
      }
      pc->data = ptr;
      pc->capacity + INC_SZ;
      printf("当前的容量大小为%d", pc->capacity);
  }
}

释放空间

void Distorycon(Contact *pc)
{
  free(pc->data);
  pc->data = NULL;
  pc->capacity= 0;
  pc->size = 0;
  printf("已释放空间........");
}

全部内容在这里--        简单的通讯录 动态实现 - 代码片段 - Gitee.com

相关文章
TU^
|
2天前
|
C语言
C语言内存函数和字符串函数模拟实现
C语言内存函数和字符串函数模拟实现
TU^
8 0
|
1天前
|
存储 程序员 编译器
C语言变量声明内存分配(转载)
C语言变量声明内存分配(转载)
7 0
|
1天前
|
存储 安全 编译器
【C语言】动态内存管理 -- -- 深入了解malloc、calloc、realloc、free、柔性数组(万字深入了解)
【C语言】动态内存管理 -- -- 深入了解malloc、calloc、realloc、free、柔性数组(万字深入了解)
9 0
【C语言】动态内存管理 -- -- 深入了解malloc、calloc、realloc、free、柔性数组(万字深入了解)
TU^
|
2天前
|
存储 C语言
C语言浮点数在内存中的存储
在C语言中,浮点数类型用float和double表示。float类型使用4个字节(32位),而double类型使用8个字节(64位)。浮点数表示的范围:float.h中定义
TU^
7 0
|
2天前
|
C语言
c语言内存函数
c语言内存函数
10 0
|
3天前
|
存储 Java
【JAVA学习之路 | 进阶篇】ArrayList,Vector,LinkedList内存解析
【JAVA学习之路 | 进阶篇】ArrayList,Vector,LinkedList内存解析
|
3天前
|
C语言
c语言:字符串和内存函数介绍-2
c语言:字符串和内存函数介绍
6 0
|
3天前
|
C语言
c语言:字符串和内存函数介绍-1
c语言:字符串和内存函数介绍
10 0
|
3天前
|
程序员 编译器 C语言
C语言——动态内存管理
C语言——动态内存管理
|
3天前
|
存储 小程序 编译器
C语言进阶—深度剖析数据在内存中的存储
C语言进阶—深度剖析数据在内存中的存储