c语言 动态内存管理

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言


一、动态内存函数的介绍

1、malloc函数

1.1函数原型

void* malloc (size_t size);

1.2函数功能

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

  1. 如果开辟成功,返回一个指向开辟好空间的指针。
  2. 如果开辟失败,返回NULL指针。
  3. 如果参数size为0,malloc的行为是未定义的,取决于编译器。
  4. 它的返回值的类型是void*,使用的时候要自己决定用什么类型。

1.3函数实例

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(5*sizeof(int));
	//如果申请失败
	if (p == NULL)
	{
		//打印错误原因
		perror("malloc:");
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0;i < 5;i++)
	{
		*(p+i) = i;
		printf("%d ", *(p+i));
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}

2、free函数

2.1函数原型

void free(void* ptr);

2.2函数功能

功能:
free函数用来释放动态开辟的内存。
提示:

  1. 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的(不合法)。
  2. 如果ptr是NULL,函数什么也不做。

3、calloc函数

3.1函数原型

void* calloc(size_t num,size_t size);

3.2函数功能

功能:为num个大小为size的元素开辟一块连续可用的空间,并且把空间的每个字节初始化为0。
提示:

  1. calloc与malloc的区别:calloc会在返回地址以前把申请的空间的每个字节初始化为全0。

3.3函数实例

#include<errno.h>
#include<stdio.h>
#include<string.h>
int main()
{
	//申请
	int* p = (int*)calloc(10, sizeof(int));
	if (NULL == p)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//使用
	for (int i = 0;i < 10;i++)
	{
		*(p + i) = i + 1;
		printf("%d ", *(p + i));
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

4、realloc函数

4.1函数原型

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

注释:

  1. ptr是要调整的内存地址
  2. size是要调整之后的新大小
  3. 返回值是调整之后的内存起始位置

4.2函数功能

功能:
对已经开辟好的动态内存重新调整大小。
提示:

  1. 如果参数ptr为NULL指针,realloc的功能就和malloc一样。
  2. realloc在调整内存空间的有两种情况:
    第一种:原有空间以后有足够大的空间,那扩展空间就直接在原有空间之后追加空间。
    第二种:原有空间以后没有足够大的空间,在堆空间上另找一个合适大小
    的连续空间来使用。这样函数返回的是一个新的内存地址。

4.3函数实例

#include<errno.h>
#include<stdio.h>
#inlcude<stdlib.h>
#inlcude<string.h>
int main()
{
	int i = 0;
	//申请动态内存
	int* p = (int*)malloc(20);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	for (i = 0;i < 5;i++)
	{
		*(p + i) = i;
	}
	//调整动态内存
	int* ptr = (int*)realloc(p, 10*sizeof(int));
	if (ptr == NULL)
	{
		perror("realloc");
		return 1;
	}
	p = ptr;
	ptr = NULL;
	for (i = 5;i < 10;i++)
	{
		*(p + i) = i + 5;
	}
	for (i = 0;i < 10;i++)
	{
		printf("%d ", *(p + i));
	}
	//释放
	free(p);
	p = NULL;

	return 0;
}

二、常见的动态内存错误

1、对NULL指针的解引用

#include<stdlib.h>
void test()
{
	int* p = (int*)malloc(INT_MAX / 4);
	*p = 20;
	free(p);
}

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

2、对动态内存开辟空间的越界访问

void test()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
		return;
	int i = 0;
	for (i = 0;i <= 10;i++)
	{
		*(p + i) = i;
	}
	free(p);
}
int main()
{
	test();
	return 0;
}

3、对非动态内存使用free释放

void test()
{
	int a = 10;
	int* p = &a;
	free(p); //错误的
}

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

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

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

void test()
{
	int* p = (int*)malloc(4);
	if (p != NULL)
	{
		*p = 10;
	}
	//没有释放动态内存
}

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

三、几道经典的笔试题

1、题目一

#include<stdio.h>
void GetMemory(char* p)
{
	p = (char*)malloc(100);
}

int main()
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "Hello world");
	printf(str);
}

1.1解析:

  1. 处在内存泄漏,GetMemory函数中,动态申请了内存,但没有释放。
  2. GetMemort函数的参数是传值调用,形参只是对实参的一份临时拷贝,因此str还是NULL指针。strcpy拷贝的时候,造成了非法访问。

1.2修改代码

#include<stdio.h>
void GetMemory(char* p)
{
	p = (char*)malloc(100);
	if (p == NULL)
		return;
	free(p);
}

int main()
{
	char* str = NULL;
	GetMemory(&str);
	strcpy(str, "Hello world");
	printf(str);
}

2、题目二

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

void test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}

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

2.1解析

  1. 返回栈空间地址的问题。GetMemory函数中创建了临时变量p数组,出了函数之后就不能使用了。

3、题目三

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;
}

3.1解析

  1. 内存泄漏:GetMemory函数中malloc申请了动态内存。没有释放

3.2修改

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

4、题目四

void test()
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}
int main()
{
	test();
	return 0;
}

4.1解析

  1. 存在非法访问。free函数已经对str释放了。但str没有置为NULL。程序会执行if语句。

4.2修改

void test()
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	str=NULL;
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}
int main()
{
	test();
	return 0;
}

四、c/c++程序的内存开辟

1、c/c++程序内存分配的几个区域

  1. 栈区:在执行函数时,函数内部局部变量的存储单元都可以在栈上创建,函数执行结束这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
  2. 堆区:一般有程序员分配释放,若程序员不释放,程序结束时可能有os回收。分配方式类似于链表。一般都是动态内存函数开辟:malloc、calloc、realloc。
  3. 数据段(静态区)存放全局变量、静态数据(static)。程序结束后由系统释放。
  4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。

2、图解

在这里插入图片描述

五、柔性数组

5.1定义

c99中,结构中的最后一个元素允许时未知的数字键,这就叫做柔性数组成员。

例如:

typedef struct st_type
{
	int i;
	int a[0];
}type_a;

有些编译器会报错无法编译可以改成:

typedef struct st
{
	int i;
	int a[];
}type_a;

5.2柔性数组的特点

  1. 结构体中的柔性数组成员前面必须至少有一个成员。
  2. sizeof计算这个结构体的大小不包括柔性数组的内存。
  3. 包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构体的大小。

例如:

#include <stdio.h>
typedef struct st
{
	int i;
	int a[];
}type_a;

int main()
{
	printf("%d\n", sizeof(type_a)); //结果是4
	return 0;
}

5.3柔性数组的使用

typedef struct st
{
	int i;
	int a[];
}type_a;

int main()
{
	int i = 0;
	type_a* p = (type_a*)malloc(sizeof(type_a) + 100 * sizeof(int));
	p->i = 100;
	for (i = 0;i < 100;i++)
	{
		p->a[i] = i;
	}
	free(p);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值