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

相关文章
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
35 3
|
7天前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
49 12
|
4天前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
68 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
28天前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
38 3
|
1月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
56 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
1月前
|
编译器 程序员 C语言
深入C语言:动态内存管理魔法
深入C语言:动态内存管理魔法
|
1月前
|
存储 程序员 编译器
C语言——动态内存管理与内存操作函数
C语言——动态内存管理与内存操作函数
|
30天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
25 0
|
1月前
|
C语言
保姆级教学 - C语言 之 动态内存管理
保姆级教学 - C语言 之 动态内存管理
20 0