C语言动态内存管理 - 动态内存函数|相关笔试题

目录

1.引入

2.动态内存函数的介绍

 2.1 mallco

2.2 free

 2.3 calloc函数

 2.4 realloc

3.几种常见的动态内存分配错误 

3.1对空指针的解引用操作 

3.2对动态开辟空间的越界访问

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

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

3.5对同一块动态内存多次释放

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

 4.经典笔试题

4.1题目一

 4.2题目二

4.3题目三

4.5题目四


1.引入

在学习动态内存管理之前,我们已经知道的内存开辟方式有:

  1. int a = 5;     //在栈空间上开辟四个字节
  2. char arr[10] = {0};     //在栈空间上开辟10个字节的连续空间

这两种方式开辟内存空间的方式不同,但有一个共同点就是他们所申请的内存的大小是固定的,无法根据程序的具体需要来分配空间,这样可能会造成内存溢出、内存浪费等问题。

为了适应程序的具体需求,我们可以通过一些动态内存函数来优化程序。 

2.动态内存函数的介绍

 2.1 mallco

函数原型:

void* malloc (size_t size);

函数所在库:

<stdlib.h>

介绍:

这个函数用于向内存申请一块连续可用的空间,申请的结果有两种情况:

  1. 系统内存足够,内存空间开辟成功,返回一个指向开辟好空间的起始位置的指针。
  2. 系统内存不足够,内存空间开辟失败,则返回一个 NULL 指针,因此malloc的返回值一定要检查是否为空

malloc的返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候由使用者自己来决定。

特别的,如果参数 size 为 0,malloc的行为标准是未定义的,取决于编译器。

2.2 free

函数原型:

`void free (void* ptr);`

函数所在库:

<stdlib.h>

介绍:

free函数在动态内存分配中至关重要,它用于将之前分配的内存块返回给操作系统,以便其他程序可以重新使用它们。这样做可以有效地管理内存,避免内存泄漏问题,并提高程序的性能和资源利用率。

使用free函数是动态内存管理的关键步骤之一,在不再需要分配的内存时务必调用该函数(尤其是在编写较大规模的项目时),以确保内存的正确释放。

特别的,如果参数 ptr 指向的空间不是动态开辟的(比如是一个确定大小的数组),那么free函数的行为是未定义的。并且,当ptr指针是空指针时,free函数什么操作也不会进行。

下面是一个动态申请内存并释放的样例代码:

#include<stdio.h>
#include<stdlib.h>

int main()
{
	int* p = (int*)malloc(40);

	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}

	free(p); //p指向的位置不存在 -> p 成了野指针
	p = NULL;

	return 0;
}

 
2.3 calloc

函数原型:

void* calloc (size_t num, size_t size);

函数所在库:

<stdlib.h>

介绍:

函数的功能是为 `num `个大小为 `size` 的元素开辟一块空间,并且把空间的每个字节初始化为0。

calloc与函数malloc的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0

 2.4 realloc

 函数原型:

void* realloc (void* ptr, size_t size);

函数所在库:

<stdlib.h>

介绍:

`ptr`:指向之前用`malloc`, `calloc`, `realloc`开辟的内存区

特别的,当`ptr`为`NULL`时,`realloc`和`malloc`的功能相同

`size` 是调整之后的新大小

realloc可以用于改变之前动态分配内存块的大小,或者在需要时申请新的内存块。

对于realloc函数的返回值,是一个指向被调整过的内存块,这个内存块既有可能是`ptr本身`也有可能是`一个新的区域`

  1. 若后面还有足够的连续的空间,足够继续开辟,那么就在后面开辟并返回原内存块的首地址
  2. 若后面连续的空间不够继续开辟了,那么就在内存中找一个位置开辟一个新的内存块,然后将旧空间中的数据拷贝到新空间并释放旧空间,返回值是新内存块的地址

我们可以调试以下代码来验证:

int main()
{
	int* p = (int*)malloc(40);

	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}

	for (int i = 0; i < 10; i++)
	{
		*(p + i) = i + 1;
	}

	int *ptr = realloc(p, 80);
	if(ptr != NULL)
	{
		p = ptr;
	}
	else
	{
		perror("realloc");
		return 1;
	}
	for (int i = 0; i < 20; i++)
	{
		printf("%d ", *(p + i));
	}

	free(p);

	return 0;
}

在监视窗口中:

当用realloc扩大较少(相对)空间时, realloc返回的指针就是p ,即p == ptr。

 当用realloc扩大较大(相对)空间时, realloc返回的指针指向的是一个新的空间, 即p != ptr

 

3.几种常见的动态内存分配错误 

3.1对空指针的解引用操作 

申请的空间过大,动态内存分配无法完成,malloc返回的是NULL指针,若进行解引用会报错

void test()
{
 int *p = (int *)malloc(INT_MAX);
 *p = 20;
 free(p);
}

3.2对动态开辟空间的越界访问

即使是动态分配的空间,大小也是有界限的,当申请的空间小于使用的空间,就会造成越界访问

void test()
{
 int i = 0;
 int *p = (int *)malloc(10*sizeof(int));
 if(NULL == p)
 {
 perror("malloc");
 }
 for(i=0; i<=10; i++)
 {
 *(p+i) = i;//当i是10的时候越界访问
 }
 free(p);
}

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

void test()
{
 int a = 10;
 int *p = &a;
 free(p);
}

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

void test()
{
 int *p = (int *)malloc(100);
 p++;
 free(p);//p不再指向动态内存的起始位置
}

3.5对同一块动态内存多次释放

void test()
{
 int *p = (int *)malloc(100);
 free(p);
 ... ...
 free(p);//重复释放
}

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

动态申请的内存空间,不会因为出        了作用域而自动销毁!

在这个程序中,malloc申请的空间发生内存泄漏,这片空间既不能释放也不能使用。

void test()
{
 int *p = (int *)malloc(100);
 if(NULL != p)
 {
 *p = 20;
 }
}
int main()
{
 test();
 while(1);

 return 0
}

 4.经典笔试题

4.1题目一

该代码能正常打印出"hello world"吗?

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

答案:不能

解析:str传给GetMemorysh函数后还是指向一个空指针,在调用strcpy时会对str进行解引用,而空指针无法解引用,程序崩溃退出。

 4.2题目二

该代码能正常打印出"hello world"吗?

char *GetMemory(void)
{
 char p[] = "hello world";
 return p;
}
void Test(void)
{
 char *str = NULL;
 str = GetMemory();
 printf(str);
}

答案: 不能

解析:p的生命周期只存在于GetMemory函数中,函数退出,p就被销毁了,再赋值给str,那么str就是一个野指针

4.3题目三

请问运行Test 函数会有什么样的结果?

void GetMemory(char **p, int num)
{
 *p = (char *)malloc(num);
}
void Test(void)
{
 char *str = NULL;
 GetMemory(&str, 100);
 strcpy(str, "hello");
 printf(str);
}

答案:没有释放申请的内存

4.5题目四

 请问运行Test 函数会有什么样的结果?

void Test(void)
{
 char *str = (char *) malloc(100);
 strcpy(str, "hello");
 free(str);
 if(str != NULL)
 {
 strcpy(str, "world");
 printf(str);
 }
}

答案:hello world 但判断逻辑有问题

解析:本题考查点在 if(str != NULL)之前要有一句 str = NULL; 这是在free后的安全做法

  • 21
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值