C语言 超详细梳理总结动态内存管理

目录
  • 一.为什么存在动态内存分配
  • 二.动态内存函数的介绍
    • 1.malloc和free
    • 2.calloc
    • 3.realloc
  • 三.常见的动态内存错误
    • 1.对NULL指针的解引用操作
    • 2.对动态开辟空间的越界访问
    • 3.对非动态开辟的空间使用free释放
    • 4.使用free释放一块动态开辟空间的一部分
    • 5.对同一块开辟的空间多次释放
    • 6.动态内存开辟忘记释放(内存泄漏)
  • 四.几个经典的笔试题

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

我们已经掌握的内存开辟方式有:

int a = 10;//在栈空间开辟4个字节的连续空间
int b[20] = { 0 };//在栈空间开辟20个字节的连续空间

这种开辟空间的方式有以下特点:

1.开辟空间的大小是固定的

2.开辟数组时必须指定大小

初学数组时,我写过下面的错误代码。

int N;
scanf("%d",&N);
int a[N]={ 0 };

可N是变量,不能用于数组元素个数的初始化。

如果我们需要的空间大小在程序运行时才能知道,那就只能试试动态内存开辟了。

二.动态内存函数的介绍

1.malloc和free

void* malloc (size_t size); 
void free (void* ptr);

malloc函数用于向内存申请一块连续可用的空间,并且返回指向这块空间的指针。

若开辟成功,返回指向这块空间的指针

若开辟失败,返回NULL指针,因此malloc的返回值一定要做检查

使用完malloc函数要用free释放申请的内存空间

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

int main()
{
	int* p = (int*)malloc(40);//开辟40个字节的栈空间
	if (p == NULL)            //检查是否为空指针
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		*(p + i)=i;
	}
	free(p);                 //用完后释放空间,注意参数为首地址
	p = NULL;                //置为空指针
}    

2.calloc

void* calloc (size_t num, size_t size)

calloc的两个参数分别为申请元素的个数和每个元素的大小,

使用和malloc差不多,但是申请的空间会被初始化为0,

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

int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d  ", *(p + i)); //输出为 10个0
	}
	free(p);
	p = NULL;
}

3.realloc

void* realloc (void* ptr, size_t size)

realloc用于重新设置要开辟内存空间的大小,可以是增大或减小

指针ptr是指向先前使用 malloc、calloc 或 realloc 分配的内存块的指针。

size 是新开辟内存空间的大小

若原空间后面未开辟的空间足够使用,则返回原先的起始地址

若原空间后面未开辟的空间不足以填满新开辟内存空间,

则会在某个地址开辟所需要的空间,free掉原空间的地址,

并且返回新的地址的起始地址

真 ·  一条龙服务

若扩容失败,会返回空指针,因此也要检查是否是空指针

三.常见的动态内存错误

1.对NULL指针的解引用操作

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

若p为空指针,则程序错误。

解决方案:检查是否为空指针

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

int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}
	for (int i = 0; i <= 10; i++)  //当i为10时,形成越界访问,程序出错
	{
		printf("%d  ", *(p + i));
	}
	free(p);
	p = NULL;
}

使用这块空间时要注意是否已经越界访问

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

    int a = 10;
	int* p = &a;
	free(p);

一执行,程序崩溃了

4.使用free释放一块动态开辟空间的一部分

void test()
{
	int* p = (int*)malloc(100);
	p++;
	free(p);
}

同样会崩溃

5.对同一块开辟的空间多次释放

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

没错,又又又崩溃了,就不上图了

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

如果使用空间后不释放,会导致内存泄漏。

内存泄漏的堆积,这会最终消耗尽系统所有的内存。

四.几个经典的笔试题

第一个

void GetMemory(char* p)            //对空指针解引用
{
	p = (char*)malloc(100);        //内存泄露
}
void test()
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}

int main()
{
	test();

}

p是str的一份临时拷贝,指向malloc申请的起始地址,

出了函数之后,内存还给系统,str仍为空指针,strcpy把“hello world”放进空指针

第二个

char *GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
void test()
{
	char* str = NULL;
	str=GetMemory();        //野指针str
	printf(str);
}

int main()
{
	test();

}

定义字符串p,并返回p的地址

但是当出了这个函数,内存还给系统,没有使用权限

指针变为

第三个

void GetMemory(char** p,int num)            //传址调用
{
	*p = (char*)malloc(num);
}
void test()
{
	char* str = NULL;
	GetMemory(&str,100);
	strcpy(str, "hello world");
	printf(str);
                                            //没有free
}

int main()
{
	test();

}

打印hello world

没有释放空间

第四个

void GetMemory(char** p,int num)
{
	*p = (char*)malloc(num);
}
void test()
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello world");
	free(str);                            //还给你,我还要用,哼~
	if (str != NULL)
	{
		strcpy(str, "!!!");
		printf(str);
	}
}

int main()
{
	test();

}

开辟100个字节的空间后,又把这块空间还给操作系统。

再次把“!!!”放进这块空间,非法修改

tips:动态内存管理是在堆区上进行的。

到此这篇关于C语言 超详细梳理总结动态内存管理的文章就介绍到这了,更多相关C语言 动态内存管理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言内存管理及初始化细节示例详解

    目录 地址空间 指针与内存关系 内存分配与初始化细节 内存泄漏 Cookie 地址空间 首先我们回味一下之前的老图,这个图由于是我手残加 ppt 即时创作,又因为是C语言入门时讲的,内容非常粗糙磕碜.要仔细研究这张图我们应该将它翻转90度会更加容易理解更贴近原理: 我们所熟知的,栈区数据存储的地址是从高地址到低地址,堆区数据存储的地址则是由低到高,而堆区下面可细分为未初始化和已初始化的全局数据区,字符常量区和代码区.而细心的你可能注意到了我代码区下面留了一撮空间代表下面还有,但这一撮属于灰色地带

  • C++内存管理介绍

    目录 1 smart_ptr概述 1.1 RAII进制 1.2 智能指针 1.3 scoped_ptr 1.4 scoped_array 1.6 shared_array 1.7 weak_ptr弱指针 2 总结 前言; C++继承了C语言的指针,一直以来指针的一些问题困扰着开发人员,常见的指针问题主要有:内存泄露.野指针.访问越界等.值得庆幸的是C++标准委员会给我们提供了auto_ptr智能指针,后面又引入了share_ptr以及weak_ptr帮助我们正确和安全的使用指针,本文主要是介绍b

  • C语言与C++中内存管理详解

    目录 内存分布 动态内存管理方式-堆区 C语言动态内存管理 C++动态内存管理 new和delete的用法 operator new与operator delete函数 new和delete的实现原理 定位new表达式 高频面试题 重点new/delete和malloc/free的区别 内存泄漏 内存分布 主要段及其分布 ​ 每个程序运行起来以后,它将拥有自己独立的虚拟地址空间.这个虚拟地址空间的大小与操作系统的位数有关系.32位硬件平台的虚拟地址空间的地址可以从0~2^32-1,即0x0000

  • C/C++内存管理详情

    目录 C/C++内存管理 1. C/C++内存分布 2. C语言中动态内存管理方式 2.1 malloc/calloc/realloc和free 3. C++内存管理方式 3.1 new/delete操作内置类型 3.2 new和delete操作自定义类型 4. operator new与operator delete函数 5. new和delete的实现原理 5.1.new 5.2.delete 5.3.new 数组 5.4.delete 数组 C/C++内存管理 内存管理是C++最令人切齿痛

  • C++内存管理详细解析

    目录 一.C++内存管理 1. new/delete表达式 2.new/delete重载 3.类内自定义allocator(per-class allocator) 二.多线程内存分配器 1.malloc/free 2.brk和mmap 三.补充知识 1.内存泄漏 2.malloc/free和new/delete的比较 3.RAII规则 一.C++内存管理 C++中有四种内存分配.释放方式: 最高级的是std::allocator,对应的释放方式是std::deallocate,可以自由设计来搭

  • C语言 动态内存管理全面解析

    目录 1. 为什么存在动态内存分配 2. 动态内存函数的介绍 2.1 malloc和free 2.2 calloc 2.3 realloc 3. 常见的动态内存错误 3.1 对NULL指针的解引用操作 3.2 对动态开辟空间的越界访问 3.3 对非动态开辟内存使用free释放 3.4 使用free释放一块动态开辟内存的一部分 3.5 对同一块动态内存多次释放 1. 为什么存在动态内存分配 *动态内存开辟在堆区* 我们已经掌握的开辟内存方式是类型直接定义变量,开辟的内存是固定的,像: int a=

  • 一文秒懂C语言/C++内存管理(推荐)

    C 语言内存管理指对系统内存的分配.创建.使用这一系列操作.在内存管理中,由于是操作系统内存,使用不当会造成毕竟麻烦的结果.本文将从系统内存的分配.创建出发,并且使用例子来举例说明内存管理不当会出现的情况及解决办法. 一.内存 在计算机中,每个应用程序之间的内存是相互独立的,通常情况下应用程序 A 并不能访问应用程序 B,当然一些特殊技巧可以访问,但此文并不详细进行说明.例如在计算机中,一个视频播放程序与一个浏览器程序,它们的内存并不能访问,每个程序所拥有的内存是分区进行管理的. 在计算机系统中

  • 超详细分析C语言动态内存管理问题

    目录 一.为什么存在动态内存的分配 二.动态内存函数的介绍 2.1 malloc和free 2.2 calloc 2.3 realloc 三.常见的动态内存错误 3.1 对NULL指针的解引用操作 3.2 对动态开辟空间的越界访问 3.3 对非动态开辟内存使用free释放 3.4 对同一块动态内存多次释放 3.5 动态开辟内存忘记释放(内存泄漏) 四.几个经典的笔试题 五.C/C++程序的内存开辟 六.柔性数组 6.1 柔性数组的特点 6.2 柔性数组的使用 6.3 柔性数组的优势 上期结束了[

  • 浅谈C++内存管理基础知识

    目录 概述 c++可用内存 c语言的可用内存 c++新增内存区域 new和malloc 智能指针引入 智能指针的实现 java延伸 java语言整体框架 java的垃圾回收机制 总结 概述 内存管理的原理庞大而复杂,然而这些都被操作系统进行了封装,并对外预留了API,这些api被c++调用,同时也被c++再次进行了封装,再面向程序员预留出了语法特性的接口,作为使用c++的程序员,我们只需要掌握c++预留的内存管理特性即可,就像我们开车不需要管变数箱.发动机是怎么变速.点火的,我们只需要掌握汽车给

  • C语言 超详细梳理总结动态内存管理

    目录 一.为什么存在动态内存分配 二.动态内存函数的介绍 1.malloc和free 2.calloc 3.realloc 三.常见的动态内存错误 1.对NULL指针的解引用操作 2.对动态开辟空间的越界访问 3.对非动态开辟的空间使用free释放 4.使用free释放一块动态开辟空间的一部分 5.对同一块开辟的空间多次释放 6.动态内存开辟忘记释放(内存泄漏) 四.几个经典的笔试题 一.为什么存在动态内存分配 我们已经掌握的内存开辟方式有: int a = 10://在栈空间开辟4个字节的连续

  • C语言 超详细梳理总结动态内存管理

    目录 一.为什么存在动态内存分配 二.动态内存函数的介绍 1.malloc和free 2.calloc 3.realloc 三.常见的动态内存错误 1.对NULL指针的解引用操作 2.对动态开辟空间的越界访问 3.对非动态开辟的空间使用free释放 4.使用free释放一块动态开辟空间的一部分 5.对同一块开辟的空间多次释放 6.动态内存开辟忘记释放(内存泄漏) 四.几个经典的笔试题 1. 一.为什么存在动态内存分配 我们已经掌握的内存开辟方式有: int a = 10://在栈空间开辟4个字节

  • C语言超详细梳理排序算法的使用

    目录 排序的概念及其运用 排序的概念 排序运用 插入排序 直接插入排序 希尔排序 选择排序 直接选择排序 堆排序 交换排序之冒泡排序 总结 排序的概念及其运用 排序的概念 排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作. 稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次 序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排 序算法

  • C语言自定义类型超详细梳理之结构体 枚举 联合体

    目录 一.什么是结构体 1.结构体实现 2.匿名结构体类型 3.结构体自引用 4.结构体的内存对齐 5.结构体位段  二.什么是枚举 1.枚举类型的定义 2.枚举的优点 三.联合(共用体) 1.什么是联合(共用体) 2.联合(共用体)的定义 3.联合(共用体)的初始化 总结 一.什么是结构体 结构是一些值的集合,这些值称为成员变量.结构的每个成员可以是不同类型的变量. //结构体声明 struct tag //struct:结构体关键字,tag:标签名,合起来是结构体类型(类型名) { memb

  • C语言 语义陷阱超详细梳理总结

    目录 1 指针与数组 2 非数组的指针 3 作为参数的数组声明 4 空指针并非空字符串 5 边界计算与不对称边界 6 求值顺序 7 整数溢出 8 为函数提供返回值 1 指针与数组 C语言中只有一维数组.数组中的元素可以是任意类型的对象,这也是多维数组构建的理论基础所在 对于一个数组,我们只能做两件事:确定该数组的大小以及获得该数组下标为0的元素的指针.任何一个数组下标运算都等同于一个对应的指针运算. 数组名代表首元素的地址,无法对其进行++或者–操作,换句话说,我们无法改变数组名(表示的值),因

  • C语言超详细讲解字符串函数和内存函数

    目录 字符串函数 长度不受限制的字符串函数 strlen strcpy strcat strcmp 长度受限制的字符串函数介绍 strncpy strncat strncmp 字符串查找以及错误报告 strstr strtok strerror 内存操作函数 memcpy memmove memcmp 字符串函数 长度不受限制的字符串函数 strlen size_t strlen ( const char * str ) 求字符串长度: 字符串以'\0' 作为结束标志,strlen函数返回的是在

  • C语言 超详细模拟实现单链表的基本操作建议收藏

    目录 1 链表的概念及结构 2 链表的分类 3 链表的实现无头+单向+非循环链表增删查改实现 3.1 链表的定义 3.2 链表数据的打印 3.3 链表的尾插 3.4 链表空间的动态申请 3.5 链表的头插 3.6 链表的尾删 3.7 链表的头删 3.8 链表任意位置的前插入 3.9 链表任意位置的后插入 3.10 链表的任意位置的删除 3.11 链表的任意位置的前删除 3.12 链表的任意位置的后删除 3.13 链表的销毁 3.14 链表的总结 1 链表的概念及结构 概念:链表是一种物理存储结构

  • C语言 超详细顺序表的模拟实现实例建议收藏

    目录 概念及结构 接口实现 1 顺序表的动态存储 2 顺序表初始化 3 顺序表的销毁 4 顺序表的尾插 5 顺序表的尾删 6 顺序表的头插 7 顺序表的头删 8 顺序表容量的检查与扩容 9 顺序表任意位置的插入 10 顺序表任意位置的删除 11 顺序表的打印 12 顺序表元素的查找 13 顺序表元素的修改 概念及结构 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储.在数组 上完成数据的增删查改. 顺序表一般可以分为: 静态顺序表:使用定长数组存储元素,元素

  • C语言 超详细讲解算法的时间复杂度和空间复杂度

    目录 1.前言 1.1 什么是数据结构? 1.2 什么是算法? 2.算法效率 2.1 如何衡量一个算法的好坏 2.2 算法的复杂度 2.3 复杂度在校招中的考察 3.时间复杂度 3.1 时间复杂度的概念 3.2 大O的渐进表示法 3.3 常见时间复杂度计算举例 4.空间复杂度 5. 常见复杂度对比 1.前言 1.1 什么是数据结构? 数据结构(Data Structure)是计算机存储.组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合. 1.2 什么是算法? 算法(Algorit

随机推荐