C语言---动态内存管理(1)https://developer.aliyun.com/article/1544468
realloc函数的使用
• realloc函数的出现让动态内存管理更加灵活。
• 有时会我们发现过去申请的空间太⼩了,有时候我们⼜会觉得申请的空间过⼤了,那为了合理的使⽤内存,我们⼀定会对内存的⼤⼩做灵活的调整。那 realloc 函数就可以做到对动态开辟内存⼤⼩的调整。
函数原型如下:void* realloc (void* ptr, size_t size);
• ptr 是要调整的内存地址
• size 调整之后新⼤⼩
• 返回值为调整之后的内存起始位置。
• 这个函数调整原内存空间⼤⼩的基础上,还会将原来内存中的数据移动到 新 的空间。
• realloc在调整内存空间的是存在两种情况:
◦ 情况1:原有空间之后有⾜够⼤的空间
◦ 情况2:原有空间之后没有⾜够⼤的空间,直接创建一个新地址,返回的是一个新地址,在返回地址后,我们对新地址进行判断,如果返回的地址不是空指针,就是说开辟成功,那么我们直接将新地址赋值给原地址,那么原地址p就指向了新开辟的空间,并且realloc会将原空间数据拷贝到新空间里面
int main() { //申请10个整型的空间 int*p=(int*)calloc(10, sizeof(int));//开辟10个大小为4个字节的空间 if (p == NULL) { perror("calloc");//打印错误信息 return 1;//直接结束 } //使用空间 for (int i = 0; i < 10; i++) { printf("%d ", p[i]); } //如果觉得空间不够了,我们想调整空间--希望变成20个整型空间 int*ptr=(int*)realloc(p, 20 * sizeof(int)); //第一个参数是我们调整的是哪一块空间,第二个参数就是我们想调整的大小 //直接将原来的空间变成20个字节 if (ptr != NULL)//说明realloc函数开辟额外的空间成功了 { p = ptr;//那么我们就将ptr赋值给p //这样的话p就拿到了新的20个字节空间的地址了 } free(p); p = NULL; return 0; } /* realloc在追加空间的时候,有两种情况 情况一:原先是10个字节,我们扩展成20个字节,我们在原有空间后面追加10个字节的内存空间。这10个内存空间之前是没人使用的 情况二:我们在后面追加10个字节,但是后面的内存空间已经被别人占了,后面的空间是不够10个字节的 //对于情况一的话:后面空间足够,直接把空间给你,返回的是这块空间起始地址 对于情况二的话:1.后续空间不够的话,realloc函数直接在内存堆区开辟一个新的20个字节满足大小的空间 2.将旧的数据拷贝到新的空间 3.realloc函数会讲旧的空间释放,旧的空间就用我们释放了,realloc帮我们释放 总之: realloc函数返回的地址可能是一个旧的地址,也可能是一个新的地址,也可能是一个空指针 那么我们应该拿什么东西进行指针接收呢? 原来的空间地址是p,假如说realloc函数开辟空间失败的话,原本我们的p还是有10个字节的空间的, 但是现在开辟失败,返回一个空指针,直接将这个空指针赋值给p了,p就是空指针了 原本地址里面的数据就不见了 就算realloc函数开辟失败了,原先的空间也不能搞丢啊 所以我们最好不拿原先的地址p接收 我们可以再专门创建一个新地地址ptr进行接收 并且我们进行判断 if (ptr != NULL)//说明realloc函数开辟额外的空间成功了 { p = ptr;//那么我们就将ptr赋值给p //这样的话p就拿到了新的20个字节空间的地址了 } 如果开辟字节空间成功,那么直接将新地址赋值给原地址,那么p就指向了新地址了 如果realloc函数在缩小空间的时候就是直接返回的是原空间的地址 realloc函数直接在原空间的基础上将多余空间舍弃 realloc函数是很好的,有了realloc函数,使我们的动态内存管理更加灵活 */
4.常见的动态内存的错误
1.
int main() { int *p=(int*)malloc(10 * sizeof(int)); if (p == NULL) { perror("malloc");//打印错误信息 return 1;//为空指针的话我们直接返回 } for (int i = 0; i < 10; i++) { printf("%d ", p[i]);//--*(p+i)//如果地址开辟失败了,返回了一个空指针,那么这里就是对空指针进行解引用操作 //如果p是空指针的话,下面的代码就全错了,所以我们在之前要进行判断 } free(p); p = NULL; return 0; } //我们在写代码的时候要写的规范点,对malloc返回值要进行判断
2.
int main() { int *p=(int*)malloc(10 * sizeof(int)); if (p == NULL) { perror("malloc");//打印错误信息 return 1;//为空指针的话我们直接返回 } for (int i = 0; i < 40; i++)//我们只生成了10个整型的空间,但是我们循环40次,以为这里是40个字节,这种情况就是越界问题了 { printf("%d ", p[i]); } free(p); p = NULL; return 0; } //访问空间在我们创造的空间内就行了,尽量不越界访问
3.
int main() { int a=10; int* p = &a;//并非动态开辟的 free(p); p = NULL; return 0; } //这种错误就是用free去释放并非动态内存开辟的空间,最后运行时会报错的
4.释放内存的时候没有从开头释放内存
int main() { int *p=(int*)malloc(10 * sizeof(int)); if (p == NULL) { perror("malloc");//打印错误信息 return 1;//为空指针的话我们直接返回 } for (int i = 0; i < 5; i++)// { *p = i; p++; }//5次循环之后,p就指向了第6个元素了 free(p);//这里的p就是第6个元素的地址了,那么我们从这里开始释放内存是不行的 //我们要释放空间必须从头开始释放 p = NULL; return 0; }
5.
//int main() //{ // int *p=(int*)malloc(10 * sizeof(int)); // if (p == NULL) // { // perror("malloc"); // return 1; // } // free(p); // p = NULL; // //………… // //再次free // free(p);//在这里的p已经是空指针了,因为已经内存释放过一次了,这里就没啥问题 // p = NULL; // return 0; //} //但是如果是下面的这种,第一次释放的时候没有将p赋值为NULL,第二次接着对p进行释放 int main() { int* p = (int*)malloc(10 * sizeof(int)); if (p == NULL) { perror("malloc"); return 1; } free(p); //………… //再次free free(p); p = NULL; return 0; } //如果第一次在内存释放的时候没有将p赋值为空指针,第二次接着用p释放内存,这样就会报错的, //这种就是对一块动态内存多次释放 //相当与对野指针进行释放 //多以在用完p释放完内存后,将p赋值为空指针很有必要的
6.在动态内存内开辟空间之后,一定要进行内存释放,不然会出现内存泄漏的问题
//多以在用完p释放完内存后,将p赋值为空指针很有必要的 void test() { int flag = 1; int* p = (int*)malloc(100);//开辟100个字节的空间 if (p == NULL) { /// return; } //不为空指针--正常使用 if (flag)//条件让我们直接返回了,那么后面的内存释放就没有进行了,没机会释放了 { return; } free(p); p = NULL; } int main() { test(); return 0; } //不进行内存释放了,除非程序运行完,否则这块空间永远找不到了 //这就叫内存泄露 /* 如果像解决我们就应该提前将这块空间地址返回去,让别人帮你释放这块空间 */
动态内存是一把双刃剑
1.提供灵活的内存管理的方式
2.带来风险
malloc calloc函数都能开辟空间
realloc函数不仅能调整空间,而且还能申请空间
int main() { /*int* p = (int*)malloc(20); realloc(p, 40);使用realloc拓展开辟额外的20个字节的空间*/ realloc(NULL, 40);//==malloc(40) //如果realloc函数的第一个参数传的是空指针的话,那么这个函数的作用和malloc函数作用是一样的了 return 0; }
如果realloc函数的第一个参数传的是空指针的话,那么这个函数的作用和malloc函数作用是一样的了
C语言---动态内存管理(3)https://developer.aliyun.com/article/1544473