一文带你搞懂C语言动态内存管理

目录
  • 一、malloc函数和free函数
  • 二、calloc函数与malloc函数的异同
  • 三、柔性数组

一、malloc函数和free函数

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

如果开辟成功,则返回一个指向开辟好空间的指针。

如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。

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

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

void* malloc (size_t size);

(2) free函数

free函数用来释放动态开辟的内存。

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

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

二、calloc函数与malloc函数的异同

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

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

(3)例如

#include<stdio.h>
int main()
{
	int* p = calloc(10, sizeof(int));
	if (NULL != p)
	{
	   //使用空间
	}

	free(p);
	p = NULL;
	return 0;
}

三、柔性数组

(1)特点

  • 结构中的柔性数组成员前面必须至少一个其他成员。2. sizeof 返回的这种结构大小不包括柔性数组的内存。
  • 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应 柔性数组的预期大小。

(2)使用优势

//代码1
#include<stdio.h>
typedef struct st_type
{
	int i;
	int a[0];//柔性数组成员
}type_a;
void main()
{
	printf("%d\n", sizeof(type_a));//输出的是4
	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);
}
//代码2
#include<stdio.h>
typedef struct st_type
{
	int i;
	int* p_a;
}type_a;
void main()
{
	type_a* p = malloc(sizeof(type_a));
	p->i = 100;
	p->p_a = (int*)malloc(p->i * sizeof(int));
	//业务处理
	for (int i = 0; i < 100; i++)
	{
		p->p_a[i] = i;
	}
	//释放空间
	free(p->p_a);
	p->p_a = NULL;
	free(p);
	p = NULL;
}

上述代码1和代码2都可以完成同样的功能,但是方法1的实现有两个好处:

1. 方便内存释放

如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。

2. 这样有利于访问速度.

连续的内存有益于提高访问速度,也有益于减少内存碎片。

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

(0)

相关推荐

  • 详解C语言中的动态内存管理

    目录 一.动态内存管理 1.1为什么要有动态内存管理 1.2动态内存介绍 1.3常见的动态内存错误 一.动态内存管理 1.1为什么要有动态内存管理 1.1.1  在c语言中我们普通的内存开辟是直接在栈上进行开辟的 int i = 20;//在栈空间上开辟四个字节 int arr[10]={0}; //在栈中连续开辟四十个字节 这样开辟的特点是: (1)他所开辟的空间是固定的 (2)数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配 但对于空间的需求,我们有的时候并不知道,有可能空间

  • C语言如何实现可变参数详解

    目录 可变参数 实现 代码 分析 关键语句 为什么 内存地址 难点 优化 总结 可变参数 可变参数是指函数的参数的数据类型和数量都是不固定的. printf函数的参数就是可变的.这个函数的原型是:int printf(const char *format, ...). 用一段代码演示printf的用法. // code-A #include <stdio.h> int main(int argc, char **argv) { printf("a is %d, str is %s,

  • C语言可变参数列表的用法与深度剖析

    目录 前言 一.可变参数列表是什么? 二.怎么用可变参数列表 三.对于宏的深度剖析 隐式类型转换 对两个函数的重新认知 总结 前言 可变参数列表,使用起来像是数组,学习过函数栈帧的话可以发现实际上他也就是在栈区定义的一块空间当中连续访问,不过他不支持直接在中间部分访问. 声明: 以下所有测试都是在x86,vs2013下完成的. 一.可变参数列表是什么? 在我们初始C语言的第一节课的时候我们就已经接触了可变参数列表,在printf的过程当中我们通常可以传递大量要打印的参数,但是我们却不知道他是如何

  • C语言进阶可变参数列表

    可变参数 可变参数是C语言提供的一种参数可变的机制,咱希望函数带有可变数量的参数,而不是预定义数量的参数.它允许咱定义一个函数,能根据具体的需求接受可变数量的参数,比如这种: int Max(int num,...) { va_list arg; va_start(arg,num); int max = va_arg(arg,int); for(int i = 1;i<num;i++) { int sid = va_arg(arg,int); } if(sid > max) { max = s

  • 深入了解C语言的动态内存管理

    目录 一.为什么会存在动态内存 二.动态内存函数 1.malloc和free 2.calloc 3.realloc 三.动态内存函数常见错误 2.对NULL指针进行解引用操作 3.使用free释放一块动态开辟内存的一部分 4.对静态内存进行free释放 5.对同一内存空间多次释放 6.动态开辟空间忘记释放 四.经典笔试题 1.笔试1 2.笔试2 3.笔试3 总结 一.为什么会存在动态内存 int data=20;//在栈空间上开辟4个字节空间 char ch[5]={0};//在栈开辟5个字节连

  • C语言可变参数函数详解

    目录 C语言可变参数函数 总结 C语言可变参数函数 C 语言允许定义参数数量可变的函数,这称为可变参数函数(variadic function).这种函数需要固定数量的强制参数(mandatory argument),后面是数量可变的可选参数(optional argument). 这种函数必须至少有一个强制参数.可选参数的类型可以变化.可选参数的数量由强制参数的值决定,或由用来定义可选参数列表的特殊值决定. C 语言中最常用的可变参数函数例子是 printf()和 scanf().这两个函数都

  • C语言的可变参数函数实现详解

    目录 1.简介 2.简单的使用方式 总结 1.简介 今天看到一个有趣的东西C语言的可变参数函数 众所周知,C语言的函数不能重载,那么你printf和scanf是怎么可以输入多个参数的 例如查看到的printf的定义为 printf(const char *_Restrict, ...); 这称为可变参数函数.这种函数需要固定数量的强制参数,后面是数量可变的可选参数 这种函数必须至少有一个强制参数.可选参数的类型可以变化.可选参数的数量由强制参数的值决定,或由用来定义可选参数列表的特殊值决定. C

  • C语言可变参数与函数参数的内存对齐详解

    目录 什么是可变参数? 使用可变参数 函数参数的内存对齐 总结 什么是可变参数? 有时,您可能会碰到这样的情况,您希望函数带有可变数量的参数,而不是预定义数量的参数. C 语言为这种情况提供了一个解决方案,它允许您定义一个函数,能根据具体的需求接受可变数量的参数. 比如我们最常用的printf函数,它的函数声明是:int printf(const char *format, ...);该函数就是一个典型的应用可变参数的实例,后面那三个...就是说明该函数是可变参数函数. 使用可变参数 要使用可变

  • C语言可变参数与内存管理超详细讲解

    目录 概述 动态分配内存 重新调整内存的大小和释放内存 概述 有时,您可能会碰到这样的情况,您希望函数带有可变数量的参数,而不是预定义数量的参数.C 语言为这种情况提供了一个解决方案,它允许您定义一个函数,能根据具体的需求接受可变数量的参数.下面的实例演示了这种函数的定义. int func(int, ... ) { . . . } int main() { func(2, 2, 3); func(3, 2, 3, 4); } 请注意,函数func()最后一个参数写成省略号,即三个点号(...)

  • 一文带你搞懂C语言动态内存管理

    目录 一.malloc函数和free函数 二.calloc函数与malloc函数的异同 三.柔性数组 一.malloc函数和free函数 (1) 这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针. 如果开辟成功,则返回一个指向开辟好空间的指针. 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查. 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定. 如果参数 size为0,malloc的行为是标准是未

  • 一文带你搞懂C语言预处理宏定义

    目录 预定义符号 #define #define 定义标识符 #define 定义宏 替换规则 # 和## 预定义符号 这些预定义符号都是语言内置的 __FILE__ //进行编译的源文件 __LINE__ //文件当前的行号 __DATE__ //文件被编译的日期 __TIME__ //文件被编译的时间 __STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义 VS环境下未定义__STDC__ ,说明Visual Studio并未完全遵循ANSI C. #define #defi

  • 一文带你搞懂Golang结构体内存布局

    目录 前言 结构体内存布局 结构体大小 内存对齐 总结 前言 结构体在Go语言中是一个很重要的部分,在项目中会经常用到,大家在写Go时有没有注意过,一个struct所占的空间不一定等于各个字段加起来的空间之和,甚至有时候把字段的顺序调整一下,struct的所占空间不一样,接下来通过这篇文章来看一下结构体在内存中是怎么分布的?通过对内存布局的了解,可以帮助我们写出更优质的代码.感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助. 结构体内存布局 结构体大小 结构体实际上就是由各种类型的数据组合而成

  • 一文带你搞懂Java中Object类和抽象类

    目录 一.抽象类是什么 二.初始抽象类 2.1 基本语法 2.2 继承抽象类 三.抽象类总结 四.Object类 4.1 初始Object 4.2 toString 4.3 equals 4.4 hashcode 一.抽象类是什么 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类. 由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用.也是因为这个原因,通常在设计阶段决定要

  • 一文带你搞懂Java中Synchronized和Lock的原理与使用

    目录 1.Synchronized与Lock对比 2.Synchronized与Lock原理 2.1 Synchronized原理 2.2 Lock原理 3.Synchronized与Lock使用 Synchronized Lock 4.相关问题 1.Synchronized与Lock对比 实现方式:Synchronized是Java语言内置的关键字,而Lock是一个Java接口. 锁的获取和释放:Synchronized是隐式获取和释放锁,由Java虚拟机自动完成:而Lock需要显式地调用lo

  • 一文带你搞懂JS中六种For循环的使用

    目录 一.各个 for 介绍 1.for 2.for ... in 3.for ... of 4.for await...of 5.forEach 6.map 二.多个 for 之间区别 1.使用场景差异 2.功能差异 3.性能差异 三.for 的使用 for 循环在平时开发中使用频率最高的,前后端数据交互时,常见的数据类型就是数组和对象,处理对象和数组时经常使用到 for 遍历,因此下班前花费几分钟彻底搞懂这 5 种 for 循环.它们分别为: for for ... in for ... o

  • 一文带你搞懂Numpy中的深拷贝和浅拷贝

    目录 1. 引言 2. 浅拷贝 2.1 问题引入 2.2 问题剖析 3. 深拷贝 3.1 举个栗子 3.2 探究原因 4. 技巧总结 4.1 判断是否指向同一内存 4.2 其他数据类型 5. 总结 1. 引言 深拷贝和浅拷贝是Python中重要的概念,本文重点介绍在NumPy中深拷贝和浅拷贝相关操作的定义和背后的原理. 闲话少说,我们直接开始吧! 2. 浅拷贝 2.1 问题引入 我们来举个栗子,如下所示我们有两个数组a和b,样例代码如下: import numpy as np a = np.ar

  • 一文带你搞懂Maven的继承与聚合

    目录 一.继承 二.继承关系实施步骤 三.聚合与继承的区别 一.继承 我们已经完成了使用聚合工程去管理项目,聚合工程进行某一个构建操作,其他被其管理的项目也会 执行相同的构建操作.那么接下来,我们再来分析下,多模块开发存在的另外一个问题,重复配置的问题,我们先来看张图: ■ spring-webmvc.spring-jdbc在三个项目模块中都有出现,这样就出现了重复的内容 ■ spring-test只在ssm_crm和ssm_goods中出现,而在ssm_order中没有,这里是部分重复的内容

  • 一文带你搞懂Spring响应式编程

    目录 1. 前言 1.1 常用函数式编程 1.2 Stream操作 2. Java响应式编程 带有中间处理器的响应式流 3. Reactor 3.1 Flux & Mono 3.2 Flux Mono创建与使用 4. WebFlux Spring WebFlux示例 基于注解的WebFlux 基于函数式编程的WebFlux Flux与Mono的响应式编程延迟示例 总结 哈喽,大家好,我是指北君. 相信响应式编程经常会在各种地方被提到.本篇就为大家从函数式编程一直到Spring WeFlux做一次

  • 一文带你搞懂Java中的泛型和通配符

    目录 概述 泛型介绍和使用 泛型类 泛型方法 类型变量的限定 通配符使用 无边界通配符 通配符上界 通配符下界 概述 泛型机制在项目中一直都在使用,比如在集合中ArrayList<String, String>, Map<String,String>等,不仅如此,很多源码中都用到了泛型机制,所以深入学习了解泛型相关机制对于源码阅读以及自己代码编写有很大的帮助.但是里面很多的机制和特性一直没有明白,特别是通配符这块,对于通配符上界.下界每次用每次百度,经常忘记,这次我就做一个总结,加

随机推荐