C语言:动态内存管理

动态内存管理

一、动态分配内存的必要性

普通内存分配

  1. 空间开辟大小是固定的。
  2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。

动态内存分配

1、空间大小可以更改。

2、空间大小可以在程序执行时再分配。

二、动态内存分配函数

(一)malloc

/ 复制代码
void* malloc (size_t size);

使用规则:

传入字节数,申请成功返回申请空间的首地址,申请失败返回空地址,因此申请空间需要进行检查。

/ 复制代码
int main() {
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL) {             // 进行检查会更安全
		perror("malloc");
		return 1;
	}
	printf("申请成功");
	free(p);                      //后面会有函数用法
	p = NULL;
	return 0;
}

(二)calloc

/ 复制代码
void* calloc (size_t num, size_t size);

使用规则:

传入申请类型个数以及一个类型的字节数,申请成功返回申请空间的首地址,申请失败返回空地址,申请空间需要进行检查。

与 malloc 不同的是calloc 会初始化每个字节为0;

/ 复制代码
int main() {
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL) {
		perror("calloc");
		return 1;
	}
	printf("申请成功");
	free(p);
	p = NULL;
	return 0;
}

(三)realloc

/ 复制代码
void* realloc (void* ptr, size_t size);

使用规则:

如果原来的内存块后面的空间足够开辟空间,那么直接在内存块后面扩展内存。否则 realloc 会另外寻找一片内存进行开辟,返回找到的地址,如果实在找不到这么大的内存,就会返回NULL。

/ 复制代码
int main() {
	int* arr = (int*)malloc(sizeof(int) * 10);
	if (arr == NULL) {
		perror("malloc\n");
		return 1;
	}
	int* p = (int*)realloc(arr, sizeof(int) * 20);
	if (p == NULL) {
		perror("realloc\n");
		return 1;
	}
	arr = p;
	free(arr);
	arr = NULL;
	return 0;
}

如果 ptr 传入NULL,他的效果就相当于 malloc。

/ 复制代码
int main() {
	int* p = (int*)realloc(NULL, sizeof(int) * 20);
	if (p == NULL) {
		perror("realloc\n");
	}
	free(p);
	p = NULL;
	return 0;
}

(四)free

/ 复制代码
void free (void* ptr);

上面的函数 malloc calloc realloc 分配了空间之后,也需要进行释放。如果不释放,可能导致内存泄漏。如果不置空,可能导致非法访问。

如果传入NULL,就不进行操作。

三、常见的错误

(一)对空指针进行解引用操作

如果分配空间时,内存不足以开辟返回NULL;不进行检查就解引用可能导致程序崩溃。

(二)对动态分配空间越界访问

顾名思义,就是不可以越界访问。

(三)free释放动态分配空间的一部分

如下,p++ 改变了 p 指向的内存,不再是首地址,就好像是释放一部分空间,这种情况程序会崩溃。

/ 复制代码
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;
	}
	free(p);
	p = NULL;
	return 0;
}

(四)动态开辟内存忘记释放

下面代码中,如果开辟成功,malloc 开辟的内存没有释放,但是 p 的地址我们在函数外面就再也找不到了,这就是内存泄漏。

/ 复制代码
void test()
{
	int flag = 1;
	int*p = (int*)malloc(100);
	if (p == NULL)
	{
		return;
	}
	if (flag)
		return;
	free(p);
	p = NULL;
}

int main()
{
	test();
	return 0;
}

四、柔性数组

(一)柔性数组的特点

1、结构中的柔性数组成员前面必须至少一个其他成员。

2、sizeof 返回的这种结构大小不包括柔性数组的内存。

3、包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

/ 复制代码
struct S {
	int n; 
	int arr[];
};

int main() {
	struct S* p = (struct S*)malloc(sizeof(struct S) + sizeof(int) * 10);
	if (p == NULL) {
		perror("malloc");
	}
	struct S* temp = (struct S*)realloc(p, sizeof(struct S) + sizeof(int) * 20);
	if (temp == NULL) {
		perror("realloc");
	}
	p = temp;
	return 0;
}

(二)柔性数组的优势

相比于上面的代码,肯定也会有疑惑,为什么不在结果体中设置一个指针变量进行动态内存开辟空间。

那我们就进行探究

/ 复制代码
struct S
{
	int n;
	int* arr;
};

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		perror("malloc");
		return 1;
	}
	int*tmp = (int*)malloc(20*sizeof(int));
	if (tmp != NULL)
	{
		ps->arr = tmp;
	}
	else
	{
		return 1;
	}
	tmp = (int*)realloc(ps->arr, 40*sizeof(int));
	if (tmp != NULL)
	{
		ps->arr = tmp;
	}
	else
	{
		perror("realloc");
		return 1;
	}
	free(ps->arr);
	ps->arr = NULL;
	free(ps);
	ps = NULL;
	return 0;
}

我们发现柔性数组的优势:
1、提高访问速度:

连续的内存有益于提高访问速度,也有益于减少内存碎片
2、方便内存释放:

如果用动态内存开辟的方式,我们要释放两次才能释放掉内存空间,因此柔性数组更优秀。

五、有趣的题目

1、求下面代码的输出结果

/ 复制代码
void GetMemory(char* p)
{
    p = (char*)malloc(100);
}

void Test(void)
{
    char* str = NULL;
    GetMemory(str);
    strcpy(str, "hello world");
    printf(str);
}

这个代码有两个问题:

1、 传入函数中的为NULL,动态分配空间GetMemory 函数结束后就无法访问到了,造成内存泄漏。

2、strcpy 函数中,如果对空指针进行解引用操作,导致程序崩溃。

可查看strcpy详解

2、求下面代码的输出结果

/ 复制代码
int main() {
	char* p = (char*)malloc(sizeof(int) * 10);
	strcpy(p, "hello");
	free(p);         //这里没有及时的将 p 置空造成了野指针
	if (p) {         //发生非法访问
		strcpy(p, "what can i see!");
	}
	return 0;

根据代码中分析,这段代码在运行时也会导致程序崩溃。

六、结束语

小伙伴看了这篇文章感觉如何呢,如果能有收获是值得高兴的,如果觉得有帮助的化,别忘记点赞支持支持哦!

相关推荐
软工菜鸡21 分钟前
预训练语言模型BERT——PaddleNLP中的预训练模型
大数据·人工智能·深度学习·算法·语言模型·自然语言处理·bert
南宫生24 分钟前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
希言JY33 分钟前
C字符串 | 字符串处理函数 | 使用 | 原理 | 实现
c语言·开发语言
午言若35 分钟前
C语言比较两个字符串是否相同
c语言
AI视觉网奇1 小时前
sklearn 安装使用笔记
人工智能·算法·sklearn
JingHongB1 小时前
代码随想录算法训练营Day55 | 图论理论基础、深度优先搜索理论基础、卡玛网 98.所有可达路径、797. 所有可能的路径、广度优先搜索理论基础
算法·深度优先·图论
weixin_432702261 小时前
代码随想录算法训练营第五十五天|图论理论基础
数据结构·python·算法·深度优先·图论
小冉在学习1 小时前
day52 图论章节刷题Part04(110.字符串接龙、105.有向图的完全可达性、106.岛屿的周长 )
算法·深度优先·图论
Repeat7151 小时前
图论基础--孤岛系列
算法·深度优先·广度优先·图论基础
小冉在学习1 小时前
day53 图论章节刷题Part05(并查集理论基础、寻找存在的路径)
java·算法·图论