【C语言】动态内存管理

我们之前开辟的空间,大小固定,且在申明数组的时候,必须指定数组的长度。但是有时候我们需要的空间大小在程序运行的时候才知道,这就得动态内存开辟出马了。


目录

1.malloc和free

2.calloc

3.realloc

4.常见动态内存错误

5.经典笔试题

//两种动态内存传参 

//非法访问(返回栈空间问题)

6.C/C++程序的内存开辟 

7.柔性数组


1.malloc和free

头文件:#include<stdlib.h>

格式:void* malloc ( size_t size ) 

           size为申请的字节大小;

功能:向内存申请一块连续可用的空间,并返回指向这块空间的指针。

没有初始化,不赋初值时默认为随机值

如果开辟成功,则返回一个指向开辟好空间的指针;
如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查;

开辟空间过大时,也会开辟失败,Not enough space。 

申请到的空间没有初始化,直接返回起始地址。

头文件:#include<stdlib.h>

格式:void free (void* ptr);

功能:释放动态开辟的空间。

如果参数ptr指向的空间不是动态开辟的,那么free的行为是未定义的;

如果参数ptr是NULL指针,那么free函数什么都不做。

#include<errno.h>
#include<string.h>
#include<stdlib.h>
int main()
{
	//申请40个字节,用来存放10个整型
	int* p = (int*)malloc(40);
	//开辟失败,用strerror报错
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//开辟成功,存放1~10
	int i = 0;
	for (i = 0; i < 10; i++)
	{
        //赋值
		*(p + i) = i + 1;
		//打印
		printf("%d ", *(p + i));
	}
	//用free来释放申请的空间
	free(p);
	p = NULL;//手动将p置为空指针
	return 0;
}

2.calloc

头文件:#include<stdlib.h>

格式:void* calloc (size_t num, size_t size);

           num是开辟的元素个数,size是每个元素的大小;

功能:申请空间后会把空间初始化为0,然后返回起始地址。

开辟内存成功或失败同malloc。

#include<errno.h>
#include<string.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	//开辟失败
	if (p == NULL )
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//开辟成功
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	free(p);
	p = NULL;
	return 0;
}

3.realloc

头文件:#include<stdlib.h>

格式:void* realloc (void* ptr, size_t size);

功能:对动态开辟的内存进行调整。

情况1:后面有足够的空间可以扩容,直接在后面续上新的空间,返回旧空间的起始地址;

情况2:后面没有足够的空间可以扩容,找一个满足空间大小的新的连续空间,把旧的空间里的数据拷贝到新的空间,并且把旧的空间释放,返回新空间的地址;

情况3:开辟失败,返回NULL。

若传的是空指针NULL,则相当于malloc。

#include<errno.h>
#include<string.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(5 * sizeof(int));
	if (NULL == p)
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p + i) = 1;
	}
	//不够,要增加5个整型的空间
	int* ptr = (int*)realloc(p, 5 * sizeof(int));
	if (ptr != NULL)//开辟成功
	{
		p = ptr;
        ptr = NULL;//更安全
	}
	//继续使用空间
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	//释放空间
	free(p);
	p = NULL;
	return 0;
}

4.常见动态内存错误

1> 对NULL指针的解引用操作:返回值一定要检查

2> 对动态开辟内存的越界访问:没开辟的内存不能访问

3> 对非动态开辟内存使用free释放:free只能释放动态开辟内存

4> 使用free释放一块动态开辟内存的一部分:释放的p指针必须指向起始地址

5> 对同一块内存多次释放:避免重复释放或在每次释放后牢记将p指针置为NULL

6> 动态开辟内存忘记释放(内存泄漏):free与内存函数一定要成对使用

5.经典笔试题

这几个题目原题(出处:《高质量的C/C++编程》)是给出错误的代码让我们改错,这里有的就直接放上正确的代码了哈~

//两种动态内存传参 

char* GetMemory1()//传值,返回地址
{
	char* p = (char*)malloc(100);
	return p;
}
void test1()
{
	char* str = NULL;
	str = GetMemory1();
	strcpy(str, "hello world!");
	printf(str);//哦吼吼还可以这样用
	free(str);//一定不能忘!
	str = NULL;
}
void GetMemory2(char** p)//传地址
{
	*p = (char*)malloc(100);
}
void test2()
{
	char* str = NULL;
	GetMemory2(&str);
	strcpy(str,"hello world!");
	printf(str);
	free(str);//一定不能忘!
	str = NULL;
}

int main()
{
	test1();
	test2();
	return 0;
}

//非法访问(返回栈空间问题)

char* GetMemory()
{
	char p[] = "hello world!";
	return p;
}
void test()
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}
int main()
{
	test();
	return 0;
}

上述代码存在问题,打印结果是一堆乱码(随机值)。

解释:进入GetMemory函数后,内存为p数组开辟了一段空间存放hello world, 并返回首元素地址p,但是在出GetMemory函数后该内存就被还给了操作系统,该数组就被销毁了,此时再利用str访问p指向的空间就会形成非法访问,打印乱码。

将 char p[] 改为 char* p 或在前面加上 static 即可成功打印。

6.C/C++程序的内存开辟 

7.柔性数组

定义:在C99标准下,结构体中的最后一个元素允许是未知大小的数组,这就是『柔性数组』成员。

特点:1> 结构中的柔性数组成员前面至少有一个其他成员;

2> sizeof 返回的这种结构体大小不包括柔性数组的内存;
3> 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<errno.h>

struct S
{
	int n;
	char arr[];//或arr[0]
	          //表示数组大小未知,不是大小无限大
};
int main()
{
	printf("%d\n", sizeof(struct S));//4
	//创建空间
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(char));//多开辟空间适应柔性数组大小
	ps->n = 100;
	int i = 0;
	//赋值
	for (i = 0; i < 10; i++)
	{
		ps->arr[i] = 'c';
	}
	//打印
	for (i = 0; i < 10; i++)
	{
		printf("%c", ps->arr[i]);//cccccccccc
	}
	//增容
	struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(char));
	if (ptr != NULL)
	{
		ps = ptr;
	}
	else
	{
		perror("realloc");
		return 1;
	}
	//使用
	printf("%d\n", sizeof(struct S));//还是4
	//释放
	free(ps);
	ps = NULL;
	return 0;
}

结束!!明天考C啦,Best wishes to me ! 

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值