#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
//int main()
//{
// //int a = 10;
// //int arr[10] = {0};
// //以上开辟好了不能再乱动
// //malloc ——开辟内存空间,返回开辟空间的起始位置,若开辟失败,返回空指针
// // void*malloc (size_t size)
// //calloc ——返回开辟好空间的起始地址,再返回之前会将这片空间初始化为全零
// // void*calloc(size_t num,size_t size) 开辟几个元素,以及每个元素的大小
// //realloc
// //free
// //以上为动态内存管理的常用函数
// int arr[10] = { 0 };//申请的空间在栈区(局部变量,形式参数)
// int *p = (int*)malloc(40);//申请的空间在堆区(malloc,calloc,realloc,free)
// if (p == NULL)
// {
// printf("%s", strerror(errno));
// return 1;
// }
// //使用
// int i = 0;
// for (i = 0;i < 10;i++)
// {
// *(p + i) = i;
// printf("%d ", *(p + i));
// }
// //没有free,不意味着内存空间就不回收了,
// //当程序退出时,系统会自动回收空间
// //内存泄露,不用又不还
//
// free(p);//把p指向的空间释放掉
// //空间允许给系统使用,但内部内容不清楚
//
// //不过操作系统不会允许无节制的申请空间
// p = NULL;
// //这样就可以防止p是野指针
// //NULL是系统找不到的空间
//
//
//
//
// return 0;
//}
//int main()
//{
// int* p = (int*)calloc(10, sizeof(int));
// if (p == NULL)
// {
// printf("%s\n", strerror(errno));
// return 1;
// }
// int i = 0;
// for (i = 0;i < 10;i++)
// {
// printf("%d ", *(p + i));
// }
// free(p);
// p = NULL;
// return 0;
//}
//想初始化,就用calloc,否则用malloc
//calloc = malloc + memset(起始位置,设定数据,类型大小)
//realloc
// void*realloc (void* ptr,size_t size)
// 指向要调整的空间的起始位置 追加后的大小
//
//int main()
//{
// int *p = (int*)malloc(40);
// if (p = NULL)
// {
// printf("%d", strerror(errno));
// return 1;
// }
// int i = 0;
// for (i = 0;i < 10; i++)
// {
// *(p + i) = i + 1;
// }
// //扩容
// int * ptr = (int*)realloc(p, 80);
// if (ptr != NULL)
// {
// p = ptr;
// }
// for (i = 0;i < 19;i++)
// {
// printf("%d ", *(p + i));
// }
// free(p);
// p = NULL;
// //不能直接用p接收,因为有可能扩容失败,导致p成为空指针
// //
// //情况一
// // 直接追加就行,空间足够
// // 情况二
// // 直接追加不够,realloc在堆上找连续的并且是空的80个字节的空间,然后把旧的空间里的内容拷贝过来
// //最后返回新的起始地址,并把旧的空间释放掉
// //若malloc过多,会导致内存碎片化,导致内存使用效率下降
// return 0;
//}
//常见的动态内存错误
//对NULL指针的解引用操作
//int main()
//{
// int *p = (int*)malloc(40);
// if (p == NULL)
// {
// printf("%s", strerror(errno));
// return 1;
// }//加上以上代码,对malloc、realloc、calloc检查
// *p = 20;
// free(p);
// p = NULL;
// return 0;
//}
//对动态开辟空间的越界访问
//int main()
//{
// int* p = (int*)malloc(40);
// if (p = NULL)
// {
// printf("%s", strerror(errno));
// return 1;
// }
// //方式
// int i = 0;
// for (i = 0;i <= 10;i++)
// {
// p[i] = i;
// //循环了11次,形成了越界访问
// }
// free(p);
// p = NULL;
// return 0;
//}
//对非动态开辟的空间进行释放
//int main()
//{
// int a = 10;
// int* p = &a;
// free(p);
// p = NULL;
// //p不是动态开辟的,p在这里指向栈区的某个位置,free作用的是堆区
// return 0;
//}
//使用free释放部分动态开辟的空间
//int main()
//{
// int*p= (int*)malloc(40);
// if (p == NULL)
// {
// return 1l;
// }
// int i = 0;
// for (i = 0;i < 10;i++)
// {
// *p= i;
// p++;
// //若想不变,可写成
// // p[i] = i;
// //这样是p不变
// }
// free(p);
// p = NULL;
// //这里就是不完全的释放,p已经不指向起始位置
// return 0;
//}
//对同一块空间多次释放
//int main()
//{
// int* p = (int*)malloc(40);
// free(p);//p的空间已经还给操作系统
// free(p);//p仍指向还过的空间
// //free后立刻将p置为空指针
//
// return 0;
//}
//动态开辟内存忘记释放,导致内存泄漏
//void test()
//{
// int* p = malloc(40);
// int flag = 0;
// scanf("%d", &flag);
// if (flag == 5)
// {
// return;
// }
// free(p);
// p = NULL;
//}
//int main()
//{
//
// return 0;
//}
//经典的笔试题解析
// 对形参的修改不影响实参
// 对指针进行传参修改,需要取指针的地址,用二级指针接受
// strcpy不能往空指针里拷贝,NULL解引用会崩溃
// printf 直接打印字符串,字符串一般使用的是首字母地址,
// printf可以直接接收首字母地址,往后一直打印到\0
//
// 函数一但return,就会把已经开辟的空间还给操作系统,这片空间不再属于当前程序,所以会让返回的指针变为野指针
// 不能返回局部变量的地址,原因就是上面
//
// 返回栈空间地址的问题,使用完栈后会进行清除
//