动态内存管理
一、相关函数原型
原则-谁申请谁释放
这类函数返回值都是 void * 可以与其他指针直接赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| SYNOPSIS #include <stdlib.h>
void *malloc(size_t size);
void free(void *ptr);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size); void *reallocarray(void *ptr, size_t nmemb, size_t size);
|
二、函数使用与注意事项
1.malloc()
实例程序–malloc()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <stdio.h> #include <stdlib.h>
int main() { int *p = NULL; p = (int *)malloc(sizeof(int));
if(p==NULL) { printf("malloc error"); exit(1); }
*p = 10; printf("%p --> %d\n",p,*p); free(p);
exit(0); }
|
实例程序(分配连续的空间内存,用于数组)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <stdio.h> #include <stdlib.h>
int main() { int *p; int num = 5; p = malloc(sizeof(int) * num); int i; for(i = 0;i<5;i++) { scanf("%d",p+i); } for(i = 0;i < 5;i++) { printf("%d ",p[i]); } printf("\n"); exit(0); }
|
2.内存泄漏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <stdio.h> #include <stdlib.h>
void func(int *p,int n) { p = malloc(n); if(p == NULL) exit(1); return; }
int main() { int num = 100; int *p = NULL;
func(p,num); free(p); return 0; }
|
该程序存在内存泄漏,func()
函数中的形式参数int *p
为局部变量,当main()
中调用该函数结束后,p的指针就会被释放,但是malloc(n)
是动态开辟的内存,只能进行主动释放,此时p指针已经被释放,不会指向此动态开辟的内存,因此这块动态开辟内存就会丢失
修改程序
方法1:二级指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <stdio.h> #include <stdlib.h>
void func(int **p,int n) { *p = malloc(n); if(*p == NULL) exit(1); return; }
int main() { int num = 100; int *p = NULL;
func(&p,num); free(p); return 0; }
|
方法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
| #include <stdio.h> #include <stdlib.h> #if 0 void func(int **p,int n) { *p = malloc(n); if(*p == NULL) exit(1); return; } #endif
void *func(int *p,int n) { p = malloc(n); if(p == NULL ) exit(1); return p; }
int main() { int num = 100; int *p = NULL;
func(p,num); free(p); return 0; }
|
3.野指针的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <stdio.h> #include <stdlib.h>
int main() { int *p = NULL; p = malloc(sizeof(int)); if(p==NULL) { printf("malloc() error! \n"); exit(1); }
*p = 10; printf("%p---->%d\n",p,*p);
free(p);
*p = 123; printf("%p---->%d\n",p,*p);
exit(0); }
|
运行结果:

在上述的运行结果,虽然free(p)
之后的结果,对*p
进行赋值,输出*p
的值以及指针变量p里面存储的地址值(发现不变,仍然指向之前动态开辟的内存空间),但是实际此时的p
已经是一个野指针(或者悬空指针),可能由于编译器的问题里面对应的地址值并没有变化,若程序在释放p之后在该地址出存储了一个特别重要的变量,这样操作将会造成很危险的结果,因此需要在释放的之后在该指针有下一个合法的指向之前对指针p
赋值为NULL
悬空指针:是指向了一个已经被释放或无效的内存地址的指针。这种情况经常发生在一些编程错误中,例如在C或C++等低级编程语言中,当对象被删除或内存被释放后,任何仍然引用该内存地址的指针都可能变成悬空指针
修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <stdio.h> #include <stdlib.h>
int main() { int *p = NULL; p = malloc(sizeof(int)); if(p==NULL) { printf("malloc() error! \n"); exit(1); }
*p = 10; printf("%p---->%d\n",*p);
free(p); p = NULL;
*p = 123; printf("%p---->%d\n",*p);
exit(0); }
|
运行结果:

上述结果出现段错误,因为free()实际并没有将该段地址中的值清除掉,而是变量p对于开辟的内存空间再也没有引用的权限,此时的指针p
以及为NULL,无法对其在进行赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <stdio.h> #include <stdlib.h>
int main() { int *p = NULL; p = malloc(sizeof(int)); if(p==NULL) { printf("malloc() error!\n"); exit(1); }
*p = 10; printf("%p---%d\n",p,*p); free(p);
p = NULL; printf("%p\n",p); exit(0); }
|
运行结果:

4. 传入的指针参数,作为临时(局部)变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <stdio.h> #include <stdlib.h> #include <stdbool.h>
bool GetElem(int arr[],int i,int *p) { int length = sizeof(arr) / sizeof(arr[0]); if(length==0 || i<1 || i>length) return false; p = &arr[i-1]; return true; }
int main() { int list[5] = {1,2,3,4,5}; int i = 2; int *p; GetElem(list,i,p); printf("%d\n",*p); exit(0); }
|
编译结果

原因: C语言中如果一个函数接受一个数组作为参数,那么数组将会被退化为指针,正确的用法不在其他函数内部使用sizeof()
函数
而是在main()
函数中将数组的长度计算出来得到`length,在进一步将length作为参数传入值函数中
运行结果:

最后得到的结果,p指针指向的是一个不确定的区块,可以将p仍然看作为野指针
原因:在调用函数GetElem()之后,传入的指针作为局部变量,函数调用完,指针p中就被释放,又被指向不确定区块
改正1,将指针变量作为返回值返回:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <stdio.h> #include <stdlib.h> #include <stdbool.h>
int* GetElem(int arr[],int i,int *p) { int length = sizeof(arr) / sizeof(arr[0]); if(length==0 || i<1 || i>length) exit(1); p = &arr[i-1]; return p; }
int main() { int list[5] = {1,2,3,4,5}; int i = 2; int *p; p = GetElem(list,i,p); printf("%d\n",*p); exit(0); }
|
改正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
| #include <stdio.h> #include <stdlib.h> #include <stdbool.h>
bool GetElem(int arr[],int i,int **p) { int length = sizeof(arr) / sizeof(arr[0]); if(length==0 || i<1 || i>length) exit(1); *p = &arr[i-1]; return true; }
int main() { int list[5] = {1,2,3,4,5}; int i = 2; int *p; GetElem(list,i,&p); printf("%d\n",*p); exit(0); }
|
例程2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <stdio.h> #include <stdlib.h>
void swap(int *p,int *q,int* c,int* d) { p = c; q = d; }
int main() { int a = 3; int b = 4; int c=5; int d=6; int *p = &a; int *q = &b;
printf("*p:%d----*q:%d\n",*p,*q); swap(p,q,&c,&d); printf("*p:%d----*q:%d\n",*p,*q); exit(0); }
|
在函数swap()中使得指针p,q重新指向c,d,此操作无效,因为传入的指针,作为局部变量,在函数中对指针进行重新指向的操作,在函数调用结束之后,指针将指向原来的值
