文章目录
前言
学习数组的时候一般会遇到这样一个问题,向一个数组中存入N个数,N<1000。由于N不确定,为了保证所有数都能存入数组不溢出,我们都会首先定义一个元素数量大于等于1000的数据,如下所示:
#include<stdio.h>
int main(void) {
int arr[1000] = { 0 };
int N = 0;
int i = 0;
printf("请输入数组的大小\n");
scanf_s("%d", &N);
printf("请输入%d个数\n", N);
for (i = 0; i < N; i++)
scanf_s("%d", &arr[i]);
return 0;
}
这样做一个坏处就是可能会浪费内存空间,因为arr数组不一定能存的满。
很自然就能想到数组大小能不能动态定义?像这样:
#include<stdio.h>
int main(void) {
int N;
int i;
int arr[N];
printf("请输入数组的大小\n");
scanf_s("%d", &N);
printf("请输入%d个数\n", N);
for (i = 0; i < N; i++)
scanf_s("%d", &arr[i]);
return 0;
}
编译会毫不犹豫地报错,提示是“应输入常量表达式”。
这是因为C89标准规定必须使用常量表达式指明数组长度。
虽然C99中可以使用变量指明数组长度,但一些编译器并不能很好地支持C99,其中就包括常用的VS2019。
总之,灵活地定义数组大小而不造成空间浪费的梦想是破碎了。
但是办法总是有的!!!办法就是今天的主角——malloc ()。
一、malloc
1、简介
隶属标准库: stdlib
原型: void *malloc(unsigned int size)
所以需要根据实际你需要的类型对其强制类型转换
返回值: 成功时,返回指向新分配内存的指针。失败时,返回空指针(NULL)
参数: size - 要分配的字节数,如malloc(10)表示申请10bytes的内存。
功能: 申请内存。需要内存的时候就用它申请,用完就扔掉了。也就是说malloc就好像一个渣男。
2、基本使用方法
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* str;
str = (int*)malloc(20);//申请20个字节的空间
for (int i = 0; i < 5; i++)
{
*(str + i) = i;
}
for (int i = 0; i < 5; i++)
{
printf("%d\n", *(str + i));
}
/* 释放已分配的内存 */
free(str);
return(0);
}
0
1
2
3
4
3、使用malloc解决前言中的问题
使用malloc就可以做到需要多少空间就申请多少空间了,记得使用完要释放空间。
#include<stdio.h>
#include<stdlib.h>
int main(void) {
int N = 0;
int i = 0;
printf("请输入数组的大小\n");
scanf_s("%d", &N);
int* arr = (int*)malloc(N * sizeof(int));
printf("请输入%d个数\n", N);
for (i = 0; i < N; i++)
scanf_s("%d", &arr[i]);
for (i = 0; i < N; i++)
printf("%d ", arr[i]);
free(arr);
return 0;
}
请输入数组的大小
4
请输入4个数
23
89
52
14
23 89 52 14
二、free函数
free函数和malloc函数是形影不离的好兄弟。
1、简介
隶属标准库: stdlib
原型: void free(void *ptr)
返回值: 无
参数: ptr – 指针指向一个要释放内存的内存块
功能: 释放之前申请的内存(回收内存)
2、基本使用方法
如上所示
三、realloc函数
1、简介
隶属标准库: stdlib
原型: void *realloc(void *ptr, size_t size)
返回值: 该函数返回一个指针 ,指向重新分配大小的内存。如果请求失败,则返回 NULL。
参数:
ptr – 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。
size – 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。
功能: 重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。
2、基本使用方法
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int* str;
/* 最初的内存分配 */
str = (int*)malloc(16);
for (int i = 0; i < 4; i++)
{
*(str + i) = i;
printf("%d ", *(str + i));
}
printf("\n");
/* 重新分配内存 */
str = (int*)realloc(str, 20);
*(str + 4) = 4;
for (int i = 0; i < 5; i++)
{
printf("%d ", *(str + i));
}
free(str);
return(0);
}
0 1 2 3
0 1 2 3 4
四、calloc函数
1、简介
隶属标准库: stdlib
原型: void *calloc(size_t nitems, size_t size)
返回值:
该函数返回一个指针,指向已分配的内存。如果请求失败,则返回 NULL。
参数:
nitems – 要被分配的元素个数。
size – 元素的大小。
功能:
分配所需的内存空间,并返回一个指向它的指针。malloc 和 calloc 之间的不同点是,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。
2、基本使用方法
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i, n;
int* a;
printf("要输入的元素个数:");
scanf_s("%d", &n);
a = (int*)calloc(n, sizeof(int));//申请n个int大小的空间
printf("输入 %d 个数字:\n", n);
for (i = 0; i < n; i++)
{
scanf_s("%d", a + i);
}
printf("输入的数字为:");
for (i = 0; i < n; i++) {
printf("%d ", *(a + i));
}
free(a); // 释放内存
return(0);
}
五、memset函数
1、简介
隶属标准库: string.h
原型: void *memset(void *str, int c, size_t n)
返回值:
该函数返回一个指针,指向str
参数:
str – 指向要填充的内存块。
c – 要被设置的值。
n – 要被设置为该值的字符数。
功能:
从str所指向的内存开始,连续n个字节,赋值为c
2、基本使用方法
六、答疑
1、malloc与calloc的区别
malloc与calloc在内存分配时,最大的区别是后者在分配时会将内存置为0,前者不会,内存里仍保留着垃圾数据。
注意:malloc与calloc申请的都是连续内存空间
他们在用法上是相似的,以分配容纳5个int整型的空间为例,代码如下
malloc用法
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p = (int*)malloc(5 * sizeof(int));
for (int i = 0; i < 5; i++)
//方法1
//p[i] = i;
//方法2
*(p + i) = i;
for (int i = 0; i < 5; i++)
printf("%d ", p[i]);
free(p);
return 0;
}
0 1 2 3 4
calloc用法
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p = (int*)calloc(5, sizeof(int));
for (int i = 0; i < 5; i++)
p[i] = i;
for (int i = 0; i < 5; i++)
printf("%d ", p[i]);
free(p);
return 0;
}
0 1 2 3 4
2、为什么malloc、calloc、realloc函数都要和free函数配合使用?
我们都知道,就是malloc动态申请一块空间后,当不再需要的时候,需要将这块空间free掉,否则会出现内存泄漏。
什么是内存泄漏?
内存泄漏就是申请了内存使用完毕又不释放掉,导致其他程序无法再重复利用这块内存,这块内存相当于被浪费掉了(例如,原本4G的内存,有1G用了没有释放,此时你只有3G可以用了)。这浪费掉的内存就跟泄漏出去的水一样收不回来了。故叫做内存泄漏。
对于有操作系统的平台,即使你申请内存使用完忘了free也没关系,操作系统会监控每一块内存使用情况,你忘了它会帮你free。
但如果没有操作系统,如一些嵌入式平台,就没有自动回收机制了。这时候没用free就会导致内存泄漏。
内存泄漏有多可怕?
假如你要重复10k次分配10M的内存,如果每次使用完内存后都用free()释放,你的程序只需要占用10M内存就能运行;但如果你不用free(),那么你的程序结束之前就会吃掉100G的内存!你的系统很可能因此而崩溃。特别是嵌入式平台,内存空间本来就小,容不得内存泄漏。
因此为了防止出现内存泄漏,free必须与malloc、calloc、realloc函数形影不离。
3、malloc、calloc、realloc函数申请的是哪个地方的内存?
申请的是堆中的内存。
关于内存空间的分布,我会在另一篇文章中讲。
参考资料
1、5分钟看懂什么是 malloc https://zhuanlan.zhihu.com/p/105090421
2、深入 malloc 函数,带你真正理解内存分配。https://blog.csdn.net/liang19890820/article/details/120559090
3、malloc函数——一个低调的C语言学习者宝藏https://zhuanlan.zhihu.com/p/197918331
4、C语言malloc与calloc区别https://blog.csdn.net/qq_42759112/article/details/124744214
5、为什么动态内存分配申请后,还要再释放?https://www.sohu.com/na/469024940_121056793