1,指针与动态内存申请的关系
释放内存是把堆区里的指针和内存端点断开,不是把值置为0;
1.1 内存四区
C语言内存四区(程序在执行的时候,内存划分为4个区域)
-
代码区:存放函数体的二进制代码,由操作系统进行管理
-
全局区:存放全局变量和静态变量以及常量
-
栈区:由编译器自动分配释放,存放函数的参数值,局部变量等(先进后出)
-
堆区:由程序员分配和释放,如果程序员不释放,程序结束后由操作系统回收释放(自由进出)
1.2申请动态内存的函数和释放内存函数
动态内存申请就是堆区内存的操作,由程序员自己申请和释放内存
C语言使用内存申请和释放函数一定要记得包含stdlib.h头文件。不包含可能会导致申请内存失败
-
malloc函数:
void* malloc(size_t _Size);
-
_Size:申请内存字节数
-
返回申请内存的首地址
-
申请内存不会做初始化
-
-
calloc函数:
void* calloc(size_t _Count,size_t _Size);
-
_Count:申请几个
-
_Size:每个占用字节数
-
申请内存并且默认初始化为0
-
-
realloc函数:
void* realloc(void* _Block,size_t _Size);
-
_Blcok: 重新申请内存的首地址,含原数据
-
_Size:重新申请内存大小,要大于原大小
-
申请失败返回空, 申请成功返回申请内存首地址
-
-
free函数:
void free(void* _Block);
-
_Block:释放的内存首地址
-
同一段内存不能被重复释放
-
哪里申请的内存就要从哪里释放,指针做了偏移一定要还原
-
释放内存后,指针要置空,防止悬浮指针存在
-
1.3 断言函数 assert( )
#include<assert.h>//断言
int main()
{
assert(false);// 判定为非的时候会让程序在这里崩掉,在申请动态内存的时候可以判定申请的是否为空,如果是就让该程序在assert()崩溃
return 0;
}u
1.4 malloc函数
#include<stdio.h>
#include <stdlib.h>
#include<assert.h>//断言
#include<stdbool.h>
void tset_malloc()
{
int* p = (int*)malloc(sizeof(int));//强制类型转换
//申请一个int变量内存,首地址返回给p
//这个是申请堆区内存
if (p == NULL)
exit(-1);//判断申请是否为空
assert(p);//判断申请是否为空
*p = 999;
printf("%d\n", *p);
free(p);
p = NULL;
//申请一段内存--->数组
int len = 0;
scanf_s("%d", &len);
int* parr = (int*)malloc(sizeof(int) * len);
assert(parr);//判断申请是否为空
//当作产生了一个int parr[len]数组去用
for (int i= 0; i < len; i++)
{i
parr[i]=i;
printf("%d\t", parr[i]);
}
printf("\n");
/*
parr ++;不再指向你申请内存首地址回导致释放问题
valid heap pointer
解决办法:
int* temp = parr;记录开始位置
后续对指针进行偏移操作就可以进行如下释放
free(temp);
temp = NULL;
*/
free(parr);
parr = NULL;
}
int main()
{
tset_malloc();
assert(1);
return 0;
}
malloc使用场景:
-
处理大量数据的时候,使用malloc函数,栈区内存会溢出,堆区内存不会出现溢出问题
int* array = (int*)malloc(size(int)*300000000); assert(array); free(array); array = NULL;
-
动态数组,可以自动增长的数组
-
函数返回指针,不会自动释放掉内存,从而解决掉返回局部变量地址的问题
char* get_data() { char str [] = "iloveyou"; int len = strlen(str)+1;//加一是有一个'\0'结束标记 char* pstr = (char*)malloc(len); strcpy_s(pstr,len,str); return pstr;//在其他地方用完后就可以把pstr给释放掉 }
1.5 calloc函数i
默认把申请的内存初始化为0
int *p = (int*)calloc(1,sizeof(int));//默认把申请的内存初始化为0
assert(p);
printf("%d\n",*p);
free(p);
p = NULL;
1.6 realloc函数
内存的自动增长
void test_realloc()
{
int* p = (int*)realloc(sizeof(int));
assert(p);
*p = 123 ;
int* temp = (int*)realloc(p,sizeof(int)*4);//做重新申请内存大小,由原来的1个内存申请到4个内存
p = temp;//把p指向temp,必须要做的操作
p[1] = 456;
p[2] = 789;
p[3] = 000;
for (int i = 0; i < 4; i++)
{
printf("%d\t", p[i]);
}
printf("\n");
free(p);//置空
p = NULL;
}
2,函数与动态内存申请
2.1一维数组的两种申请方法
//一级指针
int* create_array1D_01(int arrayNum)
{
int* p = (int*)malloc(sizeof(int) * arrayNum);
assert(p);
return p;
}//在主函数中别忘了释放
//二级指针方式
void create_array1D_02(int** array, int arrayNum)
{
*array= (int*)malloc(sizeof(int) * arrayNum);
assert(*array);
}
int main()
{
int* parr = NULL;
create_array1D_02(&parr,5);
free(parr);//申请完后可以释放掉再次申请使用
create_array1D_02(&parr,7);
free(parr);
parr = NULL;
}
2.2二维数组的申请
//一级指针可以操作多个数字,二级指针可以操作多个一级指针
int** creat_array2D_01(int row,int cols)
{
int** p = (int**)malloc(size(int*)*row);
for(int i = 0;i < row;i++)
{
//为每一个一级指针申请内存存储一段数据
p[i] = (int*)malloc(sizeof(int) * cols);
}
reurn p;
}
void print_array2D(int** array, int row,int cols)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < cols; j++)
{
array[i][j] = i * row + j;
printf("%d\t", array[i][j]);
}
printf("\n");
}
}
int main()
{
int** pp = create_array2D_01(2, 3);
print_array2D(pp, 2, 3);
free(pp);
return 0;
}
3,函数指针
3.1基础函数指针
int Max(int a,int b)
{
return a > b ? a : b;
}
int Sum(int a,int b)
{
return a + b;
}
//回调函数,以函数指针为参数的函数都算是回调函数
void print_data(int(*func)(int,int),int a,int b)
{
printf("%d\n",func(a.b));
}
//创建函数指针--->(*指针名)替换函数名
int (*pFunc)(int a,int b) = NULL;
int (*pFunc1)(int,int) = NULL;
*pFunc2 = NULL;
//函数指针调用函数
//用函数名或者&函数赋值直接当作函数名去用就OK
pFunc = Max;
pFunc = &Max; //推荐写法
//用法
result = pFunc(1,2);//推荐写法
result = (*pFunc)(1,2);
printf_data(Max,1,2)
3.2复杂函数指针
//typedef定义函数
void test_typedf()
{
//用定义的名字替换掉typedef语句中的名字
//看的时候去掉typedef即可
typedef int INT;
INT num = 123;
typedef int ARRAY[3];
ARRAY arr;
typedef int(*FUNC)(int,int);
FUNC func = NULL;
int* p ,q;//p是指针,q是整型变量
p = #
p = num;
typedef int* int_point;
int_point p1.p2;//都是int* 类型的指针
}
//返回函数指针的函数
//1.1 typedef
typedef int(*FUNC)(int ,int);
FUNC get_max(FUNC pmax,int a,int b)
{
pmax(a,b);
return pmax;
}
//1.2把函数名和参数写到返回函数指针的类型中*后面
// int(*)(int,int) get_max2(int(*)(int,int),int a,int b)
int(*get_max2(int(*pmax)(int,int),int a,int b))(int,int)
{
pmax(a,b);
return pmax;
}
int(*(*func)(int(*)(int,int)int ,int))(int,int) = &get_max;
int(*)(int,int)-->int(*(*func)int(*)(int,int)int,int)(int,int)//套了三层
//万能指针充当函数指针
void print_data
{
printf("ddd\n");
}
void* p = &printf_data;
((void*()) p) ();
p = &get_max2;
((int(*(*)(int(*)(int,int),int ,int ))(int,int)) p) = (Max,2,3);
4,指针数组
//在后面就是什么
//数组指针
int(*p)[4] = NULL;
p = (int(*)[4])mallic(sizeof(int[4]) * 2);
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 4; j++)
{
p[i][j] = i * 4 + j;
printf("%d\t", p[i][j]);
}
printf("\n");
}
//指针数组
int* parr[3] = {NULL,NULL.NULL};
char* arr[3] = {"abc","ddd","aaa"};\
for (int i = 0; i < 3; i++)
{
puts(pstr[i]);
}
//函数指针数组
int(*pfunc)(int,int) = Sum;
int(*pfunc[4])(int,int) = {Sum,Sum,Sum,Sum};//里面放的是函数
int Sum(int a, int b)
{
return a + b;
}