C语言 day11 | 我的日常分享

C语言 day11

动态内存申请

  1. 动态内存申请的相关概念
  2. 静态分配和动态分配
  3. 动态内存申请相关函数
  4. 内存泄漏

动态内存申请的相关概念

  1. 在数组一章中,介绍过数组的长度是预先定义好的,在整个程序中固定不变
  2. 但是在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定
  3. 为了解决上诉问题,C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态的分配内存空间,也可以把不在使用的空间回收再次利用

静态分配和动态分配

静态分配

  1. 在程序编译或运行过程中,按事先规定大小分配内存空间的分配方式
  2. 必须事先知道所需空间的大小
  3. 分配在栈区或全局变量区,一般以数组形式
  4. 按计划分配

动态分配

  1. 在程序运行过程中,根据需要大小自由分配所需空间
  2. 按需分配
  3. 分配在堆区,一般使用特定的函数进行分配

动态内存申请相关函数

1、分配内存空间的函数 malloc

  • 头文件:#include<stdlib.h>

  • 函数原型:void *malloc(unsigned int num_bytes); ——> (返回值void *)

  • 调用形式:(类型说明符) malloc (size); ——> (类型说明符)进行强制类型转换

  • 功能说明:

    1. 在内存的动态储存区(堆区)中分配一块长度为size字节的连续区域用来存放类型说明符指定的类型
    2. 函数原型返回void*指针,使用时必须做相应的强制类型转换
    3. 分配的内存空间内容不确定,一般使用menset初始化
  • 返回值:

    • 分配空间的起始地址(分配成功)
    • NULL(分配失败)
  • 注意:

    1. 在调用malloc之后,一定要判断一下,是否申请内存成功
    2. 如果多次malloc申请内存,每次申请的内存之间不一定是连续的,但是每个内存内部肯定是连续的

2、free函数(释放内存函数)

  • 头文件:#include<stdlib.h>
  • 函数定义:void free(void *ptr);
  • 函数说明:free函数释放ptr指向的内存
  • 注意:ptr指向的内存必须是malloc calloc relloc动态申请的内存,只能释放堆区空间

案例1:申请一个int变量的空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc,char *argv[]){

//定义一个变量去接申请好的空间的首地址
int *addr=NULL;

//申请一个int变量空间
addr = (int *)malloc(sizeof(int));//sizeof(int)兼容作用,不同平台int字节数可能不同

//判断是否申请成功
if(NULL == addr){
printf("malloc err\n");
return 0;
}

printf("%d\n",*addr);//值不确定,要进行清0

//清空
memset(addr,0,sizeof(int));//#include<string.h> 不加这个,可能会报隐形声明的警告

printf("%d\n",*addr);//值为0

//写
*addr=1000;

//读
printf("%d\n",*addr);//1000

//释放堆区空间
free(addr);
return 0;
}

运行结果:
图片

案例2:从堆区申请一个int数组,数组大小由用户决定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc,char *argv[]){
//获取数组元素个数
int n=0;
printf("请输入数组元素个数:\n");
scanf("%d",&n);

//申请内存空间
int *arr=(int *)malloc(n*sizeof(int));
if(NULL == arr){
perror("malloc");
return 0;
}

//清0
memset(arr,0,n*sizeof(int));

//键盘获取元素
int i=0;
printf("请输入%d个int型数据:\n",n);
for(i=0;i<n;i++){
scanf("%d",arr+i);
}

//输出获取的元素
for(i=0;i<n;i++){
// printf("%d ",*(arr+i));
printf("%d ",arr[i]);
}
printf("\n");

//释放堆区空间
free(arr);
return 0;
}

运行结果:
图片

3、calloc函数

  • 头文件:#include<stdlib.h>
  • 函数原型:void * calloc(size_t nmenb,size_t size);
  • 功能说明:在内存的堆中,申请nmemb块。每块大小为size个字节的连续区域
  • 函数参数:size_t实际是无符号整型,它是在头文件中,用typedef定义出来的
  • 返回值:
    • 成功:返回申请的内存的首地址
    • 失败 返回NULL
  • 例如:char *p = (char *)calloc(3,100)
  • 特点:calloc函数申请的内存中的内容为0,自动清零;而malloc申请的内存中的内容随机的,不确定的

案例3:案例2改造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc,char *argv[]){
//获取数组元素个数
int n=0;
printf("请输入数组元素个数:\n");
scanf("%d",&n);

//申请内存空间
int *arr=(int *)calloc(n,sizeof(int));
if(NULL == arr){
perror("malloc");
return 0;
}



//键盘获取元素
int i=0;
printf("请输入%d个int型数据:\n",n);
for(i=0;i<n;i++){
scanf("%d",arr+i);
}

//输出获取的元素
for(i=0;i<n;i++){
// printf("%d ",*(arr+i));
printf("%d ",arr[i]);
}
printf("\n");

//释放堆区空间
free(arr);
return 0;
}

运行结果:
图片

4、realloc函数

动态追加或减少空间

  • 头文件:#include<stdlib.h>
  • 函数原型:void * realloc(void *s,unsigned int newsize);
  • 功能说明:在原先s指向的内存基础上重新申请内存,新的内存大小为new_sizeof个字节(包含原先的内存大小!!),如果原先内存后面有足够大的空间,就追加,返回的内存地址还是跟原先的一样的;如果后面的内存不够用,则realloc函数会在堆区找一个new_size个字节的内存申请,将原先内存中的内容拷贝过来,然后释放原先的内存,最后返回新的内存地址
  • 函数参数:size_t实际是无符号整型,它是在头文件中,用typedef定义出来的
  • 返回值:新申请的内存的首地址

案例4:改造案例3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc,char *argv[]){
//获取数组元素个数
int n=0;
printf("请输入数组元素个数:\n");
scanf("%d",&n);

//申请内存空间
int *arr=(int *)calloc(n,sizeof(int));
if(NULL == arr){
perror("malloc");
return 0;
}



//键盘获取元素
int i=0;
printf("请输入%d个int型数据:\n",n);
for(i=0;i<n;i++){
scanf("%d",arr+i);
}

//输出获取的元素
for(i=0;i<n;i++){
// printf("%d ",*(arr+i));
printf("%d ",arr[i]);
}
printf("\n");

// 追加元素
int new_size=0;
printf("请输入追加元素的个数:\n");
scanf("%d",&new_size);

arr=realloc(arr,(n+new_size)*sizeof(int));
/*
一定要接一下返回值,因为
如果后面的内存不够用,则realloc函数会在堆区找一个new_size个字节的内存申请,将原先内存中的内容拷贝过来,然后释放原先的内存,最后返回新的内存地址
*/

//获取新追加的元素
printf("请输入追加的%d个元素:\n",new_size);
for(i=n;i<new_size+n;i++){
scanf("%d",arr+i);
}

//输出所有的元素
for(i=0;i<n+new_size;i++){
// printf("%d ",*(arr+i));
printf("%d ",arr[i]);
}
printf("\n");


//释放堆区空间
free(arr);
return 0;
}

运行结果:

  1. 增加元素

图片

  1. 减少元素

图片


堆区空间使用的注意事项

  1. 指向堆区空间的指向变量,不要随意的更改指向,会造成堆区空间无法释放,造成内存泄漏

  2. 不要操作已经释放了的空间,内容是不确定的

  3. 不要对堆区空间重复释放.运行时,程序会直接终止

防止多次释放的方法(坚持使用这种方式)

1
2
3
4
5
6
7
8
9
10
11
12
int *p = (int *)calloc(1,sizeof(int));

// free(p);
// free(p);
if(p != NULL){
free(p);
p=NULL;
}
if(p != NULL){
free(p);
p=NULL;
}

内存泄漏

内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。