动态版内存分配&动态内存通讯录的实现

简介: 动态版的通讯录我们可以先了解一下动态内存分配

动态版的通讯录

我们可以先了解一下动态内存分配


void* malloc (size_t size);

void* calloc (size_t num, size_t size);

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

void free (void* ptr);

C/C++库函数

这是一个网址可以查询函数的很多信息,对于使用和模拟有很大帮助(用老版好上手!)

一些常错点,避免写的时候出错


柔性数组(与本次用到的办法有点差异)


针对静态版的库函数进行改进


Ⅰ.动态内存分配

1. 为什么存在动态内存分配

是因为我们在定义变量的时候开辟空间是有限的

数组的声明必须指明大小(C99下可以用变长数组,但是数组定义后依然无法改变长度)

结构体中的数组一样无法指明大小

2. 动态内存函数

2.1 malloc and calloc and free

void* malloc (size_t size);

size_t size

总开辟空间字节个数

void* calloc (size_t num, size_t size);

size_t num size_t size

元素个数 单个元素开辟空间字节个数

开辟的空间来自于堆区1,区别于栈区的是,它是从低地址往高地址申请的,并且未初始化的时候,栈区是(dddddddd:烫烫) 堆区(cdcdcdcd:屯屯)记忆一下好,因为这样未来可以判断错误类型是不是越界访问

开辟成功,返回开辟成功的首元素地址,通过地址可以访问申请的范围(不要越界访问)

开辟失败,返回NULL,最好要判断一下哦!

对于 malloc() ,size为开辟的字节数,并且没有初始化

对于 calloc()* ,size为单个元素的字节数,num为元素个数,相乘的结果为总开辟空间的字节数,这里的参数趋向于建立数组,故初始化为0也合情合理*

void free (void* ptr);

void* ptr

开辟了的空间的首地址

ptr = NULL 时,等于什么事没干

ptr不能是野指针!

可释放堆区的对应空间,只要给首地址,它就能释放准确的空间,绝对要是首地址!!!并且释放后可能什么值都没变,但是申请的空间“还回去”了,所以不能使用


#include<stdio.h>
#include<stdlib.h>
#include<error.h>
int main()
{
    int* ptr1 = (int*)malloc(4); 
//这里必须强制类型转化为int*
    int* ptr2 = (int*)calloc(10,4);
 //这里相当于定义了一个int[10]的数组,只不过地址给了ptr2
    if(ptr1 && ptr2)
    {
        perror("开辟空间时"); //错误原因
        return 1;
    }
    free(ptr1);
    free(ptr2);
    ptr1 = NULL; ptr2 = NULL;
//释放,并且把置为空指针,以免使用导致越界访问
    return 0;
}


2.2 realloc

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


void* ptr size_t size

开辟了的空间首地址 改变后的总大小(不要乱搞)

这里的 ptr 必须是堆区的地址!也就是说必须是以上方法申请到的

如果是 NULL,则相当于malloc()

size为开辟后总大小

如果变小了,则依旧是首地址掌控这片地址,多余部分直接free掉了

如果变大了,则继续向后延申,如果该空间被“别人”申请了,则自动找一块区域,然后原地址free掉,这一块的首地址返回

这里的使用模板与malloc他们相同

3. 常见的错误

对空指针的解引用

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

一般就是用了free后的指针

另外一提,自定义函数中,申请了空间,申请的地址,在函数外部不能使用!因为调用完之后,就会被销毁

非动态内存的free释放以及多次释放,或者释放的不是首地址

忘记free,这个可能会导致内存泄漏(养成习惯!这个忘记以后大工程就会出现一些麻烦)

注意 自定义函数内申请的内存一定要“保存”下来,不然销毁了找不到申请的地址了。

4. 柔性数组

柔性数组针对的是结构体

struct stu
{
     int i;
    char ch;
    int arr[];   //只能是放在最后这个才算数,并且上面至少定义一个成员
}


这里通过测试知道了结构体类型的大小只有8,说明结构体并没有将后面的数组算进去,其实也合理,因为我们并没有规定大小

但是,【通过结构体指针我们可以访问这个数组的内容】(就是ps->arr[?]的形式,可以理解为使用“紧接着的那个地址”是arr,并且可以通过结构体访问形式得到并使用),而这个结构体指针需要指向我们自己申请的空间,通过额外申请空间代表arr数组的空间

另外一种方式很像但是不一样

struct stu
{
   int i;
   char* str;    
}


这种是不一样的

这种是将存好的地址放在成员变量里定义一个局部变量存放,必须得(有实体)只是指针指向堆区;而柔性数组是构建一块结构体类型的指针地址(栈区无实体),内容都在堆区,释放后渣都不剩

并且柔性数组并没有指明道姓给arr而是开辟空间多余部分给它

各有千秋吧

Ⅱ.对通讯录的改版

静态版地址博客

由于静态版将空间大小都写死了,要么浪费要么不够

所以有了动态版

预告:可保存版通讯录


改动一:

结构体存储方式(多了一个容量成员变量,是为了sz==capacity 的时候,能够扩大范围)

struct contact_list
{
  //struct content list[MAX+1];
  struct content* list;     //并不是柔性数组的方法
  int sz;
  int capacity;
};


改动二:

初始化 函数(初始值变小了)

void init(struct contact_list* pt)
{
  assert(pt);
  pt->sz = 0;
  //  memset(pt->list, 0, (MAX + 1) * sizeof(struct content));
  struct content* ptr = (struct content*)malloc(NUM * sizeof(struct content));
  if (ptr == NULL)
  {
  printf("init:%s", strerror(errno));
  return;
  }
  else
  {
  pt->list = ptr;
  pt->capacity = NUM;
  }
}


改动三:

增加信息 函数(满了就延申)

void add_man(struct contact_list* pt)
{
  printf("请输入你要添加人的信息(重名者需要加上编号):>\n");
  printf("姓名:>");
  scanf("%s", pt->list[pt->sz].name);
  printf("性别:>");
  scanf("%s", pt->list[pt->sz].sex);
  printf("地址:>");
  scanf("%s", pt->list[pt->sz].address);
  printf("手机号:>");
  scanf("%s", pt->list[pt->sz].phone);
  printf("年龄:>");
  scanf("%d", &pt->list[pt->sz].age);
  pt->sz++;
  system("cls");
  printf("添加成功\n");
  if (pt->sz == pt->capacity)
  {
  struct content* ptr = (struct content*)realloc(pt->list, (pt->capacity + 2) * sizeof(struct content));
  if (ptr == NULL)
  {
    perror("add_man");
    return;
  }
  else
  {
    pt->list = ptr;
    pt->capacity += 2; //***
  }
  }
}


对于其他部分,无需删改,why?2


文章到此结束!谢谢观看 —>内容参考【比特科技】

可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭🦆!


这是我的代码仓库!(在马拉圈的22.12里)代码仓库


邮箱:2040484356@qq.com


动态内存申请的空间,一般由低地址往高地址申请,这也很好理解,因为如果由高到低的话,realloc的时候,每次申请空间的首地址都要变 ↩︎


因为指针的解引用也可以用下标访问,((p+i) = p[i]) ↩︎


目录
相关文章
|
程序员 C语言
c语言:通讯录管理系统(动态分配内存版)
c语言:通讯录管理系统(动态分配内存版)
66 0
c语言:通讯录管理系统(动态分配内存版)
|
7月前
|
C语言
【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化
动态内存管理的函数有:malloc,calloc,ralloc,free,本文讲解动态内存函数和使用,如何进行动态内存管理,实现通讯录联系人容量的动态化,对常见动态内存错误进行总结。
81 0
|
6月前
|
C语言
C语言学习记录——通讯录(静态内存)
C语言学习记录——通讯录(静态内存)
37 2
|
7月前
|
C语言
c语言小课设--通讯录(动态内存管理+可持久化数据)
c语言小课设--通讯录(动态内存管理+可持久化数据)
|
7月前
|
程序员 编译器 C语言
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(下)
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free
50 0
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(下)
|
7月前
|
编译器 数据库 C语言
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(上)
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free
53 0
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(上)
|
6月前
|
存储 C语言
C语言学习记录——通讯录(动态内存)
C语言学习记录——通讯录(动态内存)
39 0
|
7月前
|
C语言 C++
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(中)
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free
43 0
|
C语言
动态内存通讯录【C语言实现】
动态内存通讯录【C语言实现】
50 0
|
程序员 编译器 C语言
C语言进阶之通讯录的实现(静态版和动态版)以及动态内存管理(下)
数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。