IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言动态内存管理 —— 仅仅四个函数而已 -> 正文阅读

[C++知识库]C语言动态内存管理 —— 仅仅四个函数而已

1.为什么存在动态内存分配

首先我们已经掌握的内存开辟方式有:定义变量、数组等。其空间都是在栈上,栈上的空间有以下几个特征:

  • 开辟的空间大小是固定的
  • 数组在声明的时候,必须指定数组的大小,这会影响内存分配给数组的空间

当我们学习实用动态内存之后,就可以不考虑上面的情况。

2.动态内存函数介绍

2.1 malloc

这是 C 语言提供的一个动态内存开辟的函数,其函数声明为:

void* malloc (size_t size);

这个函数向内存申请一块连续并且可用的空间,并返回此空间的地址(指针)。

那么 malloc 函数有以下几个规则:

  • 如果开辟成功,则返回一个开辟好的空间的指针(无类型指针)
  • 如果开辟失败,则返回一个空指针
  • 返回值(指针)的类型是 void* ,所以使用 malloc 开辟空间时要配合强制类型转换
  • 如果参数 size 为 0 ,此时的 malloc 的行为标准是未定义的,具体取决于编译器

接下来我们来使用 malloc 函数:

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main()
{
	int* arr = NULL;
	arr = (int*)malloc(40);//arr 指针指向一块 40 字节的空间地址
	if (arr == NULL)//如果是返回值是空指针
	{
		printf("%s\n", strerror(errno));//搭配 string 类函数
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		arr[i] = i;
		printf("%d ", arr[i]);
	}
	return 0;
}

2.2 calloc

此函数也是 C 语言提供的动态内存函数,原型如下:

void* calloc (size_t num,size_t size);

参数的意义为:开辟 num 个大小为 size 的空间。

此函数与 malloc 的区别在于,会在返回空间地址之前,把空间的每个字节都初始化为 0

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main()
{
	int* arr = NULL;
	arr = (int*)calloc(10, sizeof(int));
	if (arr == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

2.3 realloc

realloc 函数让动态内存管理更加灵活。有时候我们会觉得使用 malloc 或 calloc 申请的空间小了或者大了,那么 realloc 就能对申请的空间进行重新调整。其函数原型如下:

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

其参数以及返回值的意义为:

  • ptr 是要调整的内存地址
  • size 是调整之后的空间大小(注意是调整之后的空间大小)
  • 返回值为调整之后的空间地址

realloc 对重新开辟的空间有两种做法:

?

  1. 原有空间之后有足够大的空间,那么扩展的空间会直接追加上去
  2. 原有空间之后没有足够大的空间,那么 realloc 就会在堆区上重新找一块合适的连续空间

那么对于 realloc 的使用也非常简单:

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main()
{
	int* arr = NULL;
	arr = (int*)calloc(5, sizeof(int));
	if (arr == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	int* tmp = (int*)realloc(arr, 10*sizeof(int));//现在要调整为 10 个字节的大小
	if (tmp == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	else
		arr = tmp;

	return 0;
}

2.4 free

其实,在没有介绍 free 之前,上面的代码都是"错误"的。free 也是 C 语言提供的函数,是专门用来做动态内存的释放和回收的。其函数原型如下:

void free(void* ptr);

free 函数有以下几个规则:

  • 如果参数 prt 指向的空间不是动态开辟的,那 free 函数的行为是未定义的
  • 如果参数 ptr 是空指针,那么函数什么都不做

我们补全上面的代码:

malloc:

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main()
{
	int* arr = NULL;
	arr = (int*)malloc(40);//arr 指针指向一块 40 字节的空间地址
	if (arr == NULL)//如果是返回值是空指针
	{
		printf("%s\n", strerror(errno));//搭配 string 类函数
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		arr[i] = i;
		printf("%d ", arr[i]);
	}
	free(arr);
	arr = NULL;
	return 0;
}

realloc:

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main()
{
	int* arr = NULL;
	arr = (int*)calloc(5, sizeof(int));
	if (arr == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	int* tmp = (int*)realloc(arr, 10*sizeof(int));//现在要调整为 10 个字节的大小
	if (tmp == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	else
		arr = tmp;
	free(arr);
	arr = NULL;
	return 0;
}

calloc:

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main()
{
	int* arr = NULL;
	arr = (int*)calloc(10, sizeof(int));
	if (arr == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	free(arr);
	arr = NULL;
	return 0;
}

?3. 常见的动态内存错误

3.1 对空指针的解引用操作

#include <stdlib.h>
void test()
{
	int* p = (int*)malloc(INT_MAX / 4);
	*p = 20;//如果p的值是NULL,就会有问题
	free(p);
}
int main()
{
	test();
	return 0;
}

?

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

#include <stdlib.h>
void test()
{
	int i = 0;
	int* p = (int*)malloc(10 * sizeof(int));
	if (NULL == p)
	{
		exit(EXIT_FAILURE);
	}
	for (i = 0; i <= 10; i++)
	{
		*(p + i) = i;//当i是10的时候越界访问
	}
	free(p);
}
int main()
{	
	test();
	return 0;
}

这种情况编译器是不会报错的,因为我们在使用数组时经常存在这个问题。但是在运行时会发生错误。

?

3.3 对非动态开辟的空间使用 free 释放

?
#include <stdlib.h>
void test()
{
	int a = 10;
	int* p = &a;
	free(p);//ok?
}
int main()
{
	test();
	return 0;
}

?

这种情况也会出现运行错误。

?

3.4? 使用 free 释放动态内存的一部分

#include <stdlib.h>
void test()
{
	int* p = (int*)malloc(100);
	p++;
	free(p);//p不再指向动态内存的起始位置
}
int main()
{
	test();
	return 0;
}

?3.5 对用一块内存多次释放

?

#include <stdlib.h>
void test()
{
	int* p = (int*)malloc(100);
	free(p);
	free(p);//重复释放
}
int main()
{
	test();
	return 0;
}

?

3.6??动态开辟内存忘记释放

这种情况就像我们上面未介绍 free 之前时一样。

#include <stdlib.h>
void test()
{
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
}
int main()
{
	test();
	while (1);
}

虽然编译器不会报错,运行也不会报错,但是不释放内存是不可取的坏习惯

?4. 经典笔试题

4.1 题目一

#include <stdio.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;
}

?

4.2 题目二

#include <stdio.h>
#include <stdlib.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;
}

?4.3 题目三

#include <stdio.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;
}

?

4.4 题目四

#include <stdio.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;
}

?

?

?

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:02:42  更:2022-07-17 16:04:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/12 1:53:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码