前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C语言】动态内存管理

【C语言】动态内存管理

作者头像
s-little-monster
发布2024-06-06 20:49:32
800
发布2024-06-06 20:49:32
举报

一、存在动态内存分配的原因

我们已经掌握了两种内存开辟的方式

代码语言:javascript
复制
int a = 10;
int arr[3] = {0};

但是这样开辟的空间有两个特点: ①空间开辟的大小是固定的 ②数组长度大小不能改变 这样我们引入动态内存开辟,就可以实现我们自己申请和释放空间

二、malloc和free

二者都在头文件 stdlib.h 下

1、malloc

malloc是一个动态内存开辟函数

代码语言:javascript
复制
void* malloc (size_t size);

malloc向内存申请一块连续的,大小为size个字节的空间,如果开辟成功,则返回指向这块空间的指针,如果开辟失败,则返回一个NULL指针,size最好不为0,若size为0,此时malloc的行为取决于编译器,是未定义的 返回值为void* 所以在使用时要使用强制转换的方式使malloc函数知道自己开辟空间的类型

2、free

代码语言:javascript
复制
void free (void* ptr);

free用来释放动态内存

ptr指向程序员想要释放的动态开辟的内存

如果ptr指向的空间不是动态开辟的,那么它的行为将取决于编译器

如果ptr指向的是NULL指针,则free啥也不干

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
int main()
{
	int arr[5] = { 0 };
	int* ptr = NULL;
	ptr = (int*)malloc(5 * sizeof(int));
	if (NULL != ptr)//确保动态内存被开辟出来
	{
		int i = 0;
		for (i = 0; i < 5; i++)
		{
			*(ptr + i) = i;
		}
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

我们在每次使用完动态内存时都要将它释放,不然就会造成内存泄漏,搞的堆区里的内存越用越少,并且将指向这块空间的指针置为空,不然这个指针会成为野指针

三、calloc和realloc

1、calloc

代码语言:javascript
复制
void* calloc (size_t num, size_t size);

calloc与malloc相似,num是数量,size是大小,就是为num个大小为size的元素开辟一块空间

并且在返回地址前把每一个元素初始化为0(calloc与malloc唯一的区别)

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* ptr = NULL;
	ptr = (int*)calloc(5 , sizeof(int));
	if (NULL != ptr)
	{
		int i = 0;
		for (i = 0; i < 5; i++)
			printf("%d", *(ptr + i));
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

粗略的说,calloc的参数就是把malloc的参数中的 ’ * ‘ 改为’ ,',一个参数变成两个参数

realloc

realloc函数可以调整开辟的动态内存大小

代码语言:javascript
复制
void* realloc (void* ptr, size_t size);

ptr是需要被调整内存的地址

size是调整后的新大小

返回值为调整后的内存起始位置

在追加空间时,会出现两种情况:

比如说我们有一块20字节的内存空间,当我们使用realloc再次扩展20个字节的空间的时候我们会向后访问20个字节,若这个区域没有被使用,前二十个字节与这二十个字节变成一块动态内存区域,返回这个位置的起始地址;若这个区域被使用了,前二十个字节会被回收,重新在堆上合适的地方开辟一块新的动态内存区域,返回指向这个新的内存的地址

这样如果申请的空间比较大时,我们就不能直接用ptr直接接受realloc返回的地址,因为有可能堆区没有空间可以放的下这一块内存区域,会返回NULL

代码语言:javascript
复制
ptr = (int*)realloc(ptr, 1000);

应该用一个新的指针来接受realloc的返回值,如果不是NULL,再赋给ptr

代码语言:javascript
复制
int*p = NULL;
p = realloc(ptr, 1000);
if(p != NULL)
ptr = p;

四、常见的动态内存错误

1、解引用NULL

代码语言:javascript
复制
int *p = (int *)malloc(INT_MAX/4);

这时p的值为NULL,不能对p解引用

2、对非动态内存开辟内存使用free释放

3、 对动态开辟空间越界访问

4、使用free释放一块动态开辟内存的一部分

代码语言:javascript
复制
void test()
 {
 int *p = (int *)malloc(100);
 p++;
 free(p);//p不再指向动态内存的起始位置
 }

5、对同一块动态内存进行多次释放,即多次对一个地址使用free

6、忘记释放动态开辟内存,造成内存泄漏

五、常见错误

1、形参实参问题、内存泄漏问题

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void GetMemory(char* p)
{
	p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}
int main()
{
	Test();
	return 0;
}

我们进入到Test函数后,定义了一个str空指针,将其作为实参传给GetMemory函数,GetMemory用形参指针p接受,形参指针p指向在堆区开辟的动态内存空间

①p是形参,改变p没有改变str

②没有回收开辟的动态内存空间,造成内存泄漏

最后因为str还是NULL,最终printf也不会打印任何东西出来

没有释放动态内存空间,造成内存泄漏

(指针没有置为空)

2、函数空间被回收

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>
char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}
int main()
{
	Test();
	return 0;
}

进入到Test,指针str为空指针,进入GetMemory将定义一个数组p,数组中存放字符串"hello world",return p返回了第一个字符’h‘的地址,return结束后GetMemory被回收,字符串"hello world"也被回收,接受了p的str变成了野指针,打印出的是乱码

3、空指针危险

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
}
int main()
{
	Test();
	return 0;
}

进入Test,指针str为NULL,进入GetMemory,将malloc的开辟的动态内存空间的地址存到一级指针*p中 ,&str是取整个字符串的地址都用二级指针接受,一级指针就是首元素地址,故指针str指向malloc开辟的动态内存空间,这样strcpy和printf也能正常工作了

当然,这个也要经过修改,因为在GetMemory中,我们直接用 *p接受了malloc开辟的动态内存空间,但可能会开辟失败返回NULL,所以我们要加一个if语句来确保程序正确运行:

代码语言:javascript
复制
void GetMemory(char** p, int num)
{
	char* ptr = (char*)malloc(num);
	if (NULL != ptr)
	{
		*p = ptr;
	}
}

还有就是它没有释放动态内存,造成内存泄漏

(指针没有置为空)

4、使用被free过的动态内存空间指针

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}
int main()
{
	Test();
	return 0;
}

指针str指向malloc开辟动态内存函数空间,将hello复制到里边,然后释放,然后再检查str是否为NULL,再将world复制里边,打印.

首先str直接接受malloc开辟的动态内存空间的地址是不安全的,应该像上题一样修改一下,然后这块空间被free掉了,str就变成了野指针,很大可能不是NULL,那么它就会修改一个未知的地方的量为world然后打印,非常不安全

六、柔性数组

在C99中,结构中的最后一个元素如果是数组的话,可以允许它是未知大小的,叫做柔性数组成员

代码语言:javascript
复制
struct S
{
	int i;
	char a[0];//柔性数组,有的编译器上写char a[];
};

柔性数组的特点

①柔性数组不能单独存在在结构体中,前边必须有至少一个其他成员

②使用sizeof不会计算柔性数组的内存

代码语言:javascript
复制
typedef struct S
{
	int i;
	char a[0];
}s;
int main()
{
	printf("%d\n", sizeof(s));
	return 0;
}

③包含柔性数组的结构用malloc进行内存的动态分配,分配的内存应该大于结构的大小

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct S
{
	int i;
	int a[0];
}s;
int main()
{
	int i = 0;
	s* p = (s*)malloc(sizeof(s) + 100 * sizeof(int));
	p->i = 100;
	for (i = 0; i < 100; i++)
	{
		p->a[i] = i;
	}
	free(p);
	p = NULL;
	return 0;
}

今天的分享就到这里了~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、存在动态内存分配的原因
  • 二、malloc和free
    • 1、malloc
      • 2、free
      • 三、calloc和realloc
        • 1、calloc
          • realloc
          • 四、常见的动态内存错误
            • 1、解引用NULL
              • 2、对非动态内存开辟内存使用free释放
                • 3、 对动态开辟空间越界访问
                  • 4、使用free释放一块动态开辟内存的一部分
                    • 5、对同一块动态内存进行多次释放,即多次对一个地址使用free
                      • 6、忘记释放动态开辟内存,造成内存泄漏
                      • 五、常见错误
                        • 1、形参实参问题、内存泄漏问题
                          • 2、函数空间被回收
                            • 3、空指针危险
                              • 4、使用被free过的动态内存空间指针
                              • 六、柔性数组
                                • 柔性数组的特点
                                领券
                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档