一、动态内存的优点
1.可以在程序运行期间,动态的确定开辟内存的大小
2.可以开辟大的内存块
二、开辟动态内存
- malloc:分配内存块
- calloc:分配内存块,并且全部初始化为0 (一般用malloc+for代替calloc)
- realloc:调整之前申请的内存块的大小(可扩充,可缩小)
realloc缩小内存块的话,开头地址不变,并且其他不要的数据,也没有变化,只是右边界线缩小
#include<stdio.h>
#include<malloc.h>
#include<assert.h>
int main()
{
//动态开辟10个空间 int
int* p = (int*)malloc(10 * sizeof(int));
assert(p != NULL);//断言
if (p==NULL)
{
return 0;
}
//动态开辟20个空间 short
short*p1=(short*)malloc(20 * sizeof(short));
assert(p1 != NULL);
if (p1 == NULL)
{
return 0;
}
//动态开辟100个空间 double
double* p2 = (double*)malloc(100 * sizeof(double));
assert(p2 != NULL);
if (p2 == NULL)
{
return 0;
}
//动态开辟30个空间并且初始化为0 int
int* p3 = (int*)calloc(30 ,sizeof(int));
assert(p3 != NULL);
if (p3 == NULL)
{
return 0;
}
//动态开辟50个空间并且初始化为‘A’ char
char* p4 = (char*)calloc(50, sizeof(char));
assert(p4 != NULL);
if (p4 == NULL)
{
return 0;
}
for (int i = 0; i < 50; i++)
{
p4[i] = 'A'; //*(p4+i)='A';
}
//将开辟的10个内存块,扩容为40个空间
p = (int*)realloc(p, 10 * sizeof(int) * 4);
assert(p != NULL);
if (p == NULL)
{
return 0;
}
int *p5 = (int*)realloc(0, 10 * sizeof(int) * 4);//int *p5 = (int*)malloc(10 * sizeof(int));//重新找了一个空间,并没有释放之前空间
assert(p5 != NULL);
if (p5 == NULL)
{
return 0;
}
//对p4进行缩小,缩小为5个空间
p4=(char*)realloc(p4, 5 * sizeof(char));
assert(p4 != NULL);
if (p4 == NULL)
{
return 0;
}
return 0;
}
三、释放(free函数)
free(),参数一定是开辟的内存块的开始地址
函数原型:
void free(void *p);
1.free函数的必要性:如果动态内存开辟,而使用结束后,没有释放,会导致申请的这块内存没有人可以使用,一般将这快没有人可以使用的内存叫做“垃圾”,留着垃圾的函数或者程序,会导致出现内存泄露
2.一个malloc搭配一个free
3.C语言有两个东西特别危险:
- 数组越界
- 内存泄露
4.内存泄露
内存泄露或者内存碎片处理:重启
#include<malloc.h>
int main()
{
int* p = (int*)malloc(10 * sizeof(int));//动态开辟10个空间
int* q = (int*)malloc(10 * sizeof(int));
p=q;
return 0;
}
如果没有释放,则原先的空间没有使用,也不能再次分配出去,造成内存泄露,如下图所示:
#include<malloc.h>
int main()
{
int* p = (int*)malloc(10 * sizeof(int));//动态开辟10个空间
free (p); //第一次释放
int* q = (int*)malloc(10 * sizeof(int));
free (q);//第二次释放
p = q;
}
注意:要记着释放;使用malloc的时候,要记着用free函数释放,一个malloc搭配一个free
第一次释放:
第二次释放:
5.free函数的情况:
- 同一块内存连续释放
- free释放内存块A,但其参数,并不是A的开始地址
- free结束后,会造成一个悬空指针
- 一般来说,free函数执行结束后,会立刻将其变成空指针
- 悬空指针和空指针的区别:
空指针:指向0地址的指针 非NULL即真
悬空指针:(野指针),这个指针可以能会指向任何一个空间,有修改的风险,会造成不可知的错误。
#include<malloc.h>
int main()
{
int* p = (int*)malloc(10 * sizeof(int));
free (p);
p = NULL;//修改为空
free(p);
free(p);//free传入空指针会退出,释放多少次都可以,都会直接退出
}
四、函数指针
1.函数指针:本质上是一个指针,只不过指向函数而已
编译器在编译期间对函数开辟了一块空间,而这块空间的开始地址,就是它的函数指针
2.C标准规定:函数名,可以认为是其开始地址
函数指针p获取函数地址: p = &Max; == p = Max;
函数指针p怎么调用: (*p)(10,20); == p(10,20);
例1
#include<stdio.h>
int Max(int a, int b)
{
return a>b?a:b;
}
int main()
{
int(*p)(int, int); //函数指针p 的类型返回值int类型 参数列表int,int
p = &Max; //p=Max;
int tmp = (*p)(10, 20); //tmp =p (10, 20);
printf("%d\n", tmp);
}
结果:
例2 利用函数指针编写简单的四则运算
int Add(int a, int b) //加法
{
return a + b;
}
int Sub(int a, int b) //减法
{
return a - b;
}
int Mul(int a, int b) //乘法
{
return a * b;
}
int Div(int a, int b) //除法
{
if (b == 0)
{
return -1;
}
else
{
return a / b;
}
}
int Computer(int a, int b,int(*p)(int,int))
{
return p(a, b);
}
主函数:
int main()
{
printf("a+b=%d\n", Computer(10, 20, Add)); //加
printf("a-b=%d\n", Computer(10, 20, Sub)); //减
printf("a*b=%d\n", Computer(10, 20, Mul)); //乘
printf("a/b=%d\n", Computer(10, 20, Div)); //除
}
结果: