[C][动态内存分配][柔性数组]详细讲解

news2024/11/17 15:50:34

目录

  • 1.动态内存函数的介绍
    • 1.malloc
    • 2.free
    • 2.calloc
    • 4.realloc
  • 2.常见的动态内存错误
  • 3.C/C++程序的内存开辟
  • 4.柔性数组
    • 1.是什么?
    • 2.柔性数组的特点
    • 3.柔性数组的使用
    • 4.柔性数组的优势


1.动态内存函数的介绍

1.malloc

  • 函数原型void* malloc(size_t size)
  • 功能
    • malloc()向内存申请一块连续可用的空间,并返回指向这块空间的指针
  • 返回值
    • 如果开辟成功,则返回一个指向开辟好空间的指针
    • 如果开辟失败,则返回一个NULL指针
      • 因此malloc的返回值一定要做检查
  • 返回值的类型是void*,所以**malloc()并不知道开辟空间的类型**,具体在使用的时候使用者自己来决定(强制类型转换)
  • 如果参数size == 0malloc()的行为是标准是未定义的,取决于编译器

2.free

  • 函数原型void free(void* ptr)
  • 功能free()用来释放动态开辟的内存
  • 如果参数ptr指向的空间不是动态开辟的,那free()的行为是未定义的
  • 如果参数ptrNULL,则函数什么事都不做
  • 一次完整的动态内存开辟的例子
int* p = (int*)malloc(10 * sizeof(int));
if(p == NULL)
{
	perror("main"); // main: xxxx
	return 0;
}

// 使用
for(int i = 0; i < 10; i++)
{
	*(p + i) = i;
}

free(p); // 回收空间
p = NULL; // 手动把p置为NULL

2.calloc

  • 函数原型void* calloc(size_t num, size_t size)
  • 功能:为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0
  • 与函数malloc()的区别只在于calloc()会在返回地址之前把申请的空间的每个字节初始化为全0
  • 如果对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成任务

4.realloc

  • 函数原型void* realloc(void* ptr, size_t size)

  • 功能

    • realloc()的出现让动态内存管理更加灵活
    • 有时会发现过去申请的空间太小了,有时候又会觉得申请的空间过大了,一定会对内存的大小做灵活的调整,realloc()就可以做到对动态开辟内存大小的调整
  • 参数

    • ptr:要调整的内存地址
      • ptr == NULL,则realloc()作用同malloc()
    • size:调整之后新大小
  • 返回值:调整之后的内存起始位置

  • 注意:这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间

  • realloc()在调整内存空间时,存在两种情况

    1. 原有空间之后有足够大的空间
      • 要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化
    2. 原有空间之后没有足够大的空间
      • 扩展方法
        • 在堆空间上另找一个合适大小的连续空间来使用
        • 这样函数返回的是一个新的内存地址
    • 由于上述的两种情况,realloc()的使用就要注意一些
      请添加图片描述
  • 例如:为了确保内存空间成功开辟,拿一个临时指针去接收新开辟的空间的地址,再赋值,这样保证了万一没有成功开辟新的地址,而丢失了原来的地址

int* p =int*)calloc(10, sizeof(int));
if(p == NULL)
{
	perror("main");
	return 1;
}

for(int i = 0; i < 10; i++) // 使用
{
	*(p + i) = i;
}

// 这里需要p指向更大空间,realloc调整
int* ptr = (int*)realloc(p, 20 * sizeof(int));
if(ptr != NULL)
{
	p = ptr;
}

free(p);
p = NULL;

2.常见的动态内存错误

  1. 对NULL指针的解引用操作
  2. 对动态开辟空间的越界访问
  3. 使用free释放非动态开辟的空间
  4. 使用free释放动态内存中的一部分
  5. 对同一块动态开辟的空间,多次释放
  6. 动态开辟的空间忘记释放- 内存泄漏 -> 比较严重
  • 经典例子一
    • str传给GetMemory()的时候是值传递,所以GetMemory()的形参pstr的一份临时拷贝
    • GetMemory()内部动态申请空间的地址,存放在p中,不会影响外边的str,所以当GetMemory()返回之后,str依然是NULL,所以strcpy()会失败
    • GetMemory()返回之后,形参p销毁,使得动态开辟的100个字节存在内存泄漏无法释放
void GetMemory(char* p)
{
    p = (char *)malloc(100);
}

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

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

// 改进一
char* GetMemory(char* p)
{
    p = (char *)malloc(100);
    return p;
}

void Test(void)
{
    char* str = NULL;
    str = GetMemory(str);
    strcpy(str, "SnowK");
    free(str);
    str = NULL:
}

// 改进二
void GetMemory(char** p)
{
    *p = (char *)malloc(100);
}

void Test(void)
{
    char* str = NULL;
    GetMemory(&str);
    strcpy(str, "SnowK");
    free(str);
    str = NULL:
}
  • 经典例子二
    • GetMemory()内部创建的数组是在栈区上创建的
    • 出了函数,p[]的空间就还给了操作系统
    • 返回的地址是没有实际的意义,如果通过返回的地址去访问内存,就是非法访问内存
char* GetMemory()
{
	char p[] = "SnowK";
	return p;
}

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

3.C/C++程序的内存开辟

  • C/C++程序内存分配的几个区域:
    1. 栈区(stack)
      • 在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放
      • 栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限
      • 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等
    2. 堆区(heap)
      • 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收
      • 分配方式类似于链表
    3. 数据段(静态区)(static)
      • 存放全局变量、静态数据程序
      • 结束后由系统释放
    4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码
      • 实际上普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁
      • 但是被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁,所以生命周期变长
        请添加图片描述

4.柔性数组

1.是什么?

struct s
{
	int n;
	int arr[]; // 大小是未知
}

2.柔性数组的特点

  • 结构中的柔性数组成员前面必须至少一个其他成员
  • sizeof返回的这种结构大小不包括柔性数组的内存
  • 包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小
typedef struct st_type
{
	int i;
	int arr[0]; // 柔性数组成员
}type_a;

printf("%d\n", sizeof(type_a)); // 输出的是4

3.柔性数组的使用

// 期望arr的大小是10个整形
struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
ps->n = 10;

for(int i = 0; i < 10; i++)
{
	ps->arr[i] = i;
}

// 增加
struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
if(ptr != NULL)
{
	ps = ptr;
}

// 使用
// ...
// 释放
free(ps);
ps = NULL;

4.柔性数组的优势

  • 方便内存释放
    • 如果代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户
    • 用户调用free()可以释放结构体,但是用户并不知道这个结构体内的成员也需要free(),所以你不能指望用户来发现这个事
    • 所以,如果把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free()就可以把所有的内存也给释放掉
  • 这样有利于访问速度
    • 连续的内存有益于提高访问速度,也有益于减少内存碎片
      • 其实,个人觉得也没高多少,反正也避免不了要用做偏移量的加法来寻址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1710951.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从0开始学会做标书:新手学习做标书制作必修(95节课)

入门框架 电子标书 商务标书 文档排版 技术标书 实操演示 你是否也有同样的问题 1、做标书公司没人教、没人带? 2、如何看懂招标文件? 3、小白零基础能不能学习做标书? 4、商务标、技术标如何得高分? 5、做标书需要什么软件? 6、如何制作电子标书? 7、如何避…

您提供或引用的参考资料无法对应怎么解决

在创建或编辑百度百科词条时&#xff0c;经常会遇到“您提供或引用的参考资料无法对应”的问题。以下百科参考网shaoshai是解决这个问题的一些方法&#xff1a; 检查和修正参考资料 首先&#xff0c;您需要仔细检查提供的参考资料&#xff0c;确保它们与您的内容相对应。如果发…

2024HW|常见红队使用工具

目录 什么是HW&#xff1f; 什么是网络安全红蓝对抗&#xff1f; 红队 常见工具 信息收集工具 Nmap 简介 漏洞扫描工具 Nessus简介 AWVS 简介 抓包工具 Wireshark简介 TangGo 简介 web 应用安全工具 Burpsuite 简介 SQLMap webshell 管理工具 蚁剑 冰蝎 后…

在豆包这事上,字节看得很明白

大数据产业创新服务媒体 ——聚焦数据 改变商业 导语&#xff1a; 1.基于豆包的话炉/猫箱APP市场反响一般 2.价格战对于豆包来说是副产物 3.价格战对大模型市场是良性的 4.豆包接下来会推广至国际社会 因为宣称价格比行业便宜99.3%&#xff0c;豆包成功出圈了。根据火山引擎公…

ADS基础教程17 - 创建含参子图

设计加密保护IP 一、引言二、参数设计 一、引言 将一个子图内部元器件的参数设置成可以在外部进行修改的参数&#xff0c;能够使得封装的子图更加灵活和通用。 二、参数设计 (1)打开一个子图&#xff0c;在菜单栏中选择File–>Design Parameters… (2)弹出的对话框中&am…

Aleth-NeRF: Illumination Adaptive NeRF with Concealing Field Assumption

Abstract Aleth-NeRF: 带有隐蔽场假设的照明自适应 NeRF 照明照明标准的神经辐射场(NeRF)范例采用了一种以观察者为中心的方法,将光照和材料反射的各个方面仅仅从3D 点发射纠缠在一起。这种简化的渲染方法在准确建模在不利光照条件下捕获的图像方面提出了挑战,如弱光或过度曝…

牛客网刷题 | BC101 翻转直角三角形图案

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 KiKi学习了循环&am…

【动态规划】速解简单多状态类问题

目录 17.16 按摩师 题⽬描述&#xff1a; 解法&#xff08;动态规划&#xff09;&#xff1a; 1. 状态表⽰&#xff1a; 2. 状态转移⽅程&#xff1a; 3. 初始化&#xff1a; 4. 填表顺序 5. 返回值 代码 总结&#xff1a; 213.打家劫舍II&#xff08;medium&#x…

实践部署 浦语·灵笔2 模型,写作图文并茂的文章

1 初步介绍 XComposer2 相关知识 浦语灵笔2 是基于 书生浦语2 大语言模型研发的突破性的图文多模态大模型&#xff0c;具有非凡的图文写作和图像理解能力&#xff0c;在多种应用场景表现出色&#xff0c;总结起来其具有&#xff1a; 自由指令输入的图文写作能力&#xff1a; 浦…

解读makefile中的延迟变量与即时变量

在 Makefile 中&#xff0c;有两种类型的变量&#xff1a;即时变量&#xff08;immediate variable&#xff09;和延迟变量&#xff08;deferred variable&#xff09;。 它们在 Makefile 的执行过程中具有不同的特性和行为。 即时变量&#xff08;Immediate Variable&#x…

adb 连接机顶盒命令

抓机顶盒日志的方法&#xff0c;使用此命令进行抓日志&#xff0c;个别无法抓日志的盒子可以使用此方法 1、安卓9.0版本查询命令 ps -ef |grep com.cm.webos.iptv 2、安卓4.4版本查询命令 ps |grep com.cm.webos.iptv 3、查询顺序&#xff1a;首先进入shell下进行操作 adb she…

php 连接sqlserver步骤

1.首先要确定使用的是sqlserver的哪个版本&#xff0c;比如sqlserver2012 2.确定服务器是64位还是32位的 3.确认一下使用php的哪个版本&#xff0c;比如php7.1 SQL Server 的 Microsoft PHP 驱动程序 Microsoft Drivers for PHP 支持矩阵 - PHP drivers for SQL Server | Mi…

基于遗传优化的货柜货物摆放优化问题求解matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于遗传优化的货柜货物摆放优化问题求解matlab仿真。在一个货架上&#xff0c;初始状态下&#xff0c;随机将货物放在货柜上&#xff0c;优化之后&#xff0c;整…

javaIO流知识点概况

一、前言&#xff1a; 1.1.流的概念: java将输入与输出比喻为"流"&#xff0c;英文:Stream. 就像生活中的"电流","水流"一样,它是以同一个方向顺序移动的过程.只不过这里流动的是字节(2进制数据).所以在IO中有输入流和输出流之分,我们理解他们…

GO语言 gin框架 简述

原文地址 基本路由 Go语言中文文档 一、简介 Gin是一个golang的轻量级web框架&#xff0c;性能不错&#xff0c;API友好。 Gin支持Restful风格的API&#xff0c;可以直接从URL路径上接收api参数或者URL参数&#xff0c;也可是使用json或者表单 数据绑定的方式接收参数。 Gin响…

152.找出峰值(力扣)

代码解决 class Solution { public:// 函数用于找到山峰元素的索引vector<int> findPeaks(vector<int>& mountain) {vector<int> result; // 用于存储山峰元素的索引// 遍历数组&#xff0c;从第二个元素到倒数第二个元素for(int i 1; i 1 < mount…

C++ | Leetcode C++题解之第117题填充每个节点的下一个右侧节点指针II

题目&#xff1a; 题解&#xff1a; class Solution { public:void handle(Node* &last, Node* &p, Node* &nextStart) {if (last) {last->next p;} if (!nextStart) {nextStart p;}last p;}Node* connect(Node* root) {if (!root) {return nullptr;}Node *…

数据结构——二叉树的基本应用

在此之前我们已经初步了解了二叉树&#xff0c;在介绍堆的基本应用时&#xff0c;我们已经具体介绍了完全二叉树的基本应用&#xff0c;本章我们介绍二叉树的基本应用&#xff0c;这个不止指的是完全二叉树&#xff0c;而是指泛型的二叉树。 二叉树的基本应用&#xff0c;由于…

NDIS协议驱动(三)

协议驱动程序源自发送请求并处理基础驱动程序的接收指示。 在单个函数调用中&#xff0c;NDIS 协议驱动程序可以在每个 NET_BUFFER_LIST 结构上发送具有多个 NET_BUFFER 结构的多个NET_BUFFER_LIST结构。 在接收路径中&#xff0c;协议驱动程序可以接收NET_BUFFER_LIST结构的列…

宝塔部署Java+Vue前后端分离项目

1. 服务器 服务器选择Linux的CentOS7的版本 2. 宝塔Linux面板 2.1 百度搜索宝塔 2.2 进去之后点击立即免费安装 2.3 选择Linux在线安装&#xff0c;输入服务器信息进行安装(也可以选择其他方式) 安装完成之后会弹一个宝塔的应用面板&#xff0c;并附带有登录名称和密码&…