C语言动态内存空间分配

1. 前言

在讲内存分配前,咱来聊一下为什么会有内存分配这个概念呢,大家都知道C语言当中是有着许多的数据类型,使用这些数据类型就会在内存上开辟其相对应的空间,那既然会开辟相应的空间,为什么还会有内存分配呢,我们会发现C语言分配内存空间,是已经被固定好了的,固定开辟多少空间,但是这样开辟空间会不会太死板了呢,如果我用不上这些空间呢,这样会导致内存的利用率很低,有没有一种,我需要多少空间,就开辟多少空间的方法呢,这个时候C语言就为我们引进了动态内存分配的这个概念,顾名思义,就是你想分配多少空间就分配多少空间,下边我们来一起了解一下怎样在内存上开辟我们想要的空间

(注意:使用这些函数都需要包含#include<stdlib.h>这个头文件)

2. malloc函数:

2.1 malloc函数的声明:

函数向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针(由于不知道你要申请的类型,使用返回值为void*)

  1. 如果开辟成功,则返回⼀个指向开辟好空间的指针。
  2. 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查
  3. 如果参数 size 为0,malloc的⾏为是标准是未定义的取决于编译器决于编译器(vs2022)

所以一个malloc的使用可以分为以下几步:

  1. 使用malloc函数对内存空间申请指定空间
  2. 判断返回值是否为NULL
  3. 内存空间的释放

2.2 malloc函数的简单应用:

#include <stdlib.h>
int main()
{
	int* p = (int*)malloc(sizeof(int) * 5); //向内存申请5个int型的内存空间
	if (p == NULL)  //判断返回值是否为空
	{
		perror("malloc");  //为空,则报错malloc函数处有问题
		return 1;
	}
	free(p);  //释放内存,在下边会说喔
    p = NULL;  //将p指针赋为NULL,防止出现野指针
	return 0;
}

3. free函数:

在前边的代码当中,我们了解到free函数是专门用来释放++动态申请的内存空间++,同时也说明了它只能释放动态申请的内存空间,关于这一点兄弟们不要搞混咯

3.1 free函数声明:

  1. 如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的
  2. 如果参数 ptr 是NULL指针,则函数什么事都不做

关于free函数的使用前面有提到,有需要的兄弟可以往上翻翻

4. malloc与free的实际运用:

int main()
{
	int* p = (int*)malloc(sizeof(int) * 10);//申请10个int型元素
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)  //对malloc申请的空间进行赋值
	{
		*(p + i) = i;
	}
	for (int i = 0; i < 10; i++)  //输出
	{
		printf("%d ", *(p + i));
	}
	free(p);  //释放空间
	p = NULL;
	return 0;
}
//执行结果
0 1 2 3 4 5 6 7 8 9

5. calloc函数:

5.1 calloc函数的声明:

calloc

复制代码
void* calloc (size_t num, size_t size);
  1. 与malloc相似,也是用来开辟内存空间
  2. 开辟num个size大小的元素
  3. 相较于malloc来说,calloc开辟好空间后,还会将空间内的每个字节初始化为0

5.2 calloc的简单应用:

下边我们来一段代码调试进行了解:

int main()
{
	int* p1 = (int*)calloc(10, sizeof(int));
	if (p1 == NULL)
	{
		perror("calloc");
		return 1;
	}
	free(p1);
	return 0;
}

题解分析:

6 realloc函数:

当你觉得动态内存申请的空间,不够使用的时候,这个时候就可以运用realloc函数对内存空间进行扩容,所以realloc函数是对动态内存空间进行扩容的

6.1 realloc函数的声明:

realloc

复制代码
void* realloc (void* ptr, size_t size);
  1. ptr 是要调整的内存地址
  2. size 为调整之后新大小
  3. 返回值为调整之后的内存起始位置
  4. 这个函数调整原内存空间⼤⼩的基础上,还会将原来内存中的数据移动到新的空间

6.2 relloc函数扩容内存时的两种情况:

  1. 原有的空间在扩容的时候,后边有足够的空间
  1. 原有的空间在扩容的时候,后边没有足够的空间

6.3 relloc函数的简单应用:

int main()
{
	int* p = (int*)malloc(sizeof(int) * 10);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	printf("\n");
	int* p1 = (int*)realloc(p,sizeof(int) * 15);  //扩容5个int型
	if (p1 == NULL)
	{
		perror("realloc");
		return 2;
	}
	for (int i = 10; i < 15; i++)
	{
		*(p1 + i) = i;
	}
	for (int i = 0; i < 15; i++)
	{
		printf("%d ", *(p1 + i));
	}
	free(p1);
	p1 = NULL;
	return 0;
	return 0;
}
//执行结果
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

6.4 柔性数组:

在c99中规定,一个结构体中最后的数组如果没有指定大小,则这个数组被称为柔性数组

柔性数组的特点:

  1. 结构中的柔性数组成员前⾯必须⾄少⼀个其他成员
  2. sizeof 返回的这种结构⼤⼩不包括柔性数组的内存
  3. 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小

举例:

struct S
{
	int a;
	char a1;
	int a3[];
}q;

int main()
{
	printf("%结构体大小为:zd", sizeof(q)); //计算结构体大小,了解柔性数组是否分配了大小
	struct S* s = (struct S*)malloc(sizeof(struct S) + sizeof(int) * 5); //为a3数组分配了5个int型的空间
    return 0;
}

题解分析:

6.4.1 柔性数组的简单应用:

申请一块动态内存空间后,对齐扩容,扩大柔性数组的内存空间,然后对柔性数组进行赋值,最后输出结构体中成员的值:

//柔性数组
struct S
{
	int a;
	char a1;
	int a3[];
}q;
	int main()
	{
		int ret = sizeof(struct S);
		struct S* p2 = (struct S*)malloc(sizeof(struct S));
	
		if (p2 == NULL)
		{
			perror("malloc");
			return 1;
		}
		p2->a = 100;
		p2->a1 = 48;
	
		struct S* p3 = (struct S*)realloc(p2, sizeof(struct S)+sizeof(int)*5); //扩容柔性数组的大小
		printf("%zd\n", sizeof(p3));
		if (p3 == NULL)
		{
			perror("realloc");
			return 2;
		}
		p2 = NULL;
		for (int i = 0; i < 5; i++)
		{
			p3->a3[i] = i;
		}
	
		printf("%d\n", p3->a);
		printf("%d\n", p3->a1);
		for (int i = 0; i < 5; i++)
		{
			printf("%d ", p3->a3[i]);
	
		}
		free(p3);
		p3 = NULL;
		return 0;
	}
//执行结果
8
100
48
0 1 2 3 4

7. 常见的动态内存错误:

7.1 使用free对非动态内存进行释放:

int main()
{
	int a = 10;
	int* p = &a;
	free(p); //free对非动态内存申请的空间进行释放
	return 0;
}

执行结果:

7.2 动态内存越界访问:

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);//向内存动态申请5个int型空间
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 6; i++) //向p执行的空间放入6个int型元素
	{
		*(p + i) = i;
	}
	return 0;
}

执行结果:

(程序死循环加上报错)

7.3 使⽤free释放⼀块动态开辟内存的⼀部分:

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 5; i++) 
	{
		*p = i;
		p++;   //每执行一次p++,则p指向的地址加1
	}
	free(p);
	return 0;
}

题解分析:

执行结果:

(程序死循环加上报错)

7.4 对同⼀块动态内存多次释放:

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	free(p);
	free(p);
	return 0;
}

执行结果:

(程序死循环加上报错)

7.5 动态开辟内存忘记释放(内存泄漏):

什么意思呢,简单来讲,就是你申请了空间,但是没有及时的将空间释放掉,与上边多次释放刚好相反:

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//free(p);  //为注释掉的内容
	//free(p);
	return 0;
}

执行以后不会报错,但是会造成内存上的浪费

7.6 对NULL指针的解引⽤操作:

int main()
{
	int* p = (int*)malloc(INT_MAX*10);
	*p = 100;
	free(p);
	return 0;
}

由于并没有判断p是否为NULL,当p为NULL时,就会发生以下报错:

(死循环直至程序崩溃)

(今日分享到此结束,如觉得对您有帮助,还请点赞关注支持一下,Thanks♪(・ω・)ノ!!!)

相关推荐
上海_彭彭21 分钟前
【提效工具开发】Python功能模块执行和 SQL 执行 需求整理
开发语言·python·sql·测试工具·element
3345543229 分钟前
element动态表头合并表格
开发语言·javascript·ecmascript
沈询-阿里33 分钟前
java-智能识别车牌号_基于spring ai和开源国产大模型_qwen vl
java·开发语言
残月只会敲键盘1 小时前
面相小白的php反序列化漏洞原理剖析
开发语言·php
ac-er88881 小时前
PHP弱类型安全问题
开发语言·安全·php
ac-er88881 小时前
PHP网络爬虫常见的反爬策略
开发语言·爬虫·php
爱吃喵的鲤鱼1 小时前
linux进程的状态之环境变量
linux·运维·服务器·开发语言·c++
DARLING Zero two♡1 小时前
关于我、重生到500年前凭借C语言改变世界科技vlog.16——万字详解指针概念及技巧
c语言·开发语言·科技
Gu Gu Study1 小时前
【用Java学习数据结构系列】泛型上界与通配符上界
java·开发语言
芊寻(嵌入式)2 小时前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习