#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>

//////////动态内存函数
//
//栈区 : 局部变量,函数的形参
//堆区 : 动态内存分配(malloc,free,calloc,realloc)
//静态区:全局变量,静态变量,局部静态变量(static)


//头文件<stdlib.h>
//malloc--动态内存开辟
//free--释放开辟的动态内存
//
// void* malloc (size_t size);
// void* free (void* ptr);
//
//malloc这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
// 如果开辟成功,则返回一个指向开辟好空间的指针。
// 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
// 返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
// 如果参数size为0,malloc的行为是标准是未定义的,取决于编译器。
//free 函数用来释放动态开辟的内存。
// 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。
// 如果参数ptr是NULL指针,则函数什么事都不做。
//int main()
//{
// //向内存申请10个整形的空间
// int* p = (int*)malloc(10 * sizeof(int));
// if (p == NULL)
// {
// //打印错误原因
// printf("%s\n", strerror(errno));
// }
// else
// {
// //正常开辟
// int i = 0;
// for (i = 0; i < 10; i++)
// {
// *(p + i) = i;
// printf("%d ", *(p + i));
// }
// }
// //当动态申请的空间不再使用的时候,就应该还给操作系统。
// free(p);
// //释放后P没有变化,还是能找到那块空间,为防止破坏那块空间,我们主动把p赋值为NULL。
// p = NULL;
// return 0;
//}


//calloc--动态内存开辟(初始化0)
//
// void* calloc (size_t num , size_t size);
//
//calloc函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0.
//int main()
//{
// //向内存申请10个整形的空间
// int* p = (int*)calloc(10 , sizeof(int));
// if (p == NULL)
// {
// //打印错误原因
// printf("%s\n", strerror(errno));
// }
// else
// {
// //正常开辟
// int i = 0;
// for (i = 0; i < 10; i++)
// {
// printf("%d ", *(p + i));
// }
// }
// //释放空间
// free(p);
// p = NULL;
// return 0;
//}


//realloc--动态内存调整
//
//void* realloc (void* ptr,size_t size);
//
//realloc函数的出现让动态内存管理更加灵活。
// 有时候我们发现过去申请的空间太小,有时候我们又觉得申请的空间过大了,
// 那为了合理的适合内存,我们一定会对内存的大小做灵活的调整
//ptr是要调整的内存地址
//size是调整之后新大小
//返回值为调整之后的内存起始位置
//int main()
//{
// //向内存申请20个字节的空间
// int* p = (int*)malloc(20);
// if (p == NULL)
// {
// //打印错误原因
// printf("%s\n", strerror(errno));
// }
// else
// {
// //正常开辟
// int i = 0;
// for (i = 0; i < 5; i++)
// {
// *(p + i) = i;
// printf("%d ", *(p + i));
// }
// }
// printf("\n");
// //20个字节不够用时
// //把p调整为40个字节
// int*ptr = realloc(p, 40);
// //判断调整后的空间是否是NULL
// if (ptr != NULL)
// {
// p = ptr;
// int i = 0;
// for (i = 5; i < 10; i++)
// {
// *(p + i) = i;
// printf("%d ", *(p + i));
// }
// }
// free(p);
// p = NULL;
//
// //realloc也可以直接开辟空间
// int* p1 = realloc(NULL, 40);//==malloc(40)
// free(p1);
// p1 = NULL;
//
// return 0;
//}
//realloc使用的注意事项:
// 1.如果p指向的空间之后有足够的内存空间可以追加,则直接追加,然后返回p。
// 2.如果p指向的空间之后没有足够的内存空间可以追加,则realloc函数会重新找一个新的内存区域
// 开辟一块满足需求的空间,并且把原来内存中的数据拷贝回来,释放旧的内存空间
// 最后返回新开辟的内存空间地址
// 3.得用一个新的变量来接受realloc函数的返回值


//常见的动态内存错误
//int main()
//{
// //1.对NULL进行解引用操作
//
// //int* p = (int*)malloc(40);
// ////malloc可能开辟失败,p就被赋值NULL
// //*p = 0; //这里就形成了对NULL指针p的解引用
// //free(p);
// //p = NULL;
//
// //2.对动态开辟的内存的越界访问
//
// //int* p = (int*)malloc(5 * sizeof(int));
// //if (p != NULL)
// //{
// // int i = 0;
// // //我们只开辟了5个整形的空间
// // //i<10就造成了越界访问
// // for (i = 0; i < 10; i++)
// // {
// // *(p + i) = i;
// // }
// //}
// //free(p);
// //p = NULL;
//
// //3.对非动态开辟内存的free
//
// //int a = 10;
// //int* p = &a;
// ////这里的p不是动态内存
// //*p = 20;
// //free(p);
// //p = NULL;
//
// //4.使用free释放动态开辟内存的一部分
//
// //int* p = (int*)malloc(40);
// //if (p == NULL)
// //{
// // return 0;
// //}
// //int i = 0;
// //for (i = 0; i < 10; i++)
// //{
// // *p++ = i;
// //}
// ////这时候p已经经过10次++指向了其他地方
// ////这时候使用free释放的p就不是我们开辟的动态内存空间
// //free(p);
// //p = NULL;
//
// //5.对同一块动态内存的多次释放
//
// //int* p = (int*)malloc(40);
// //if (p == NULL)
// //{
// // return 0;
// //}
// ////使用......
// ////释放
// ////free(p);
// ////......
// ////free(p);
// ////为了防止这种情况,我们在释放后最好把p赋值为NULL
// //free(p);
// //p = NULL;
// //free(p);
//
// //6.动态开辟内存忘记释放(内存泄漏)
//
// //while (1)
// //{
// // malloc(1);
// //}
// //忘记释放不再使用的动态开辟的空间会造成内存泄漏。
// //切记:动态开辟的空间一定要释放,并且正确释放。
//
// return 0;
//}