重生之我在异世界学编程之C语言:深入动态内存管理收尾 + 柔性数组篇

大家好,这里是小编的博客频道
小编的博客:就爱学编程

很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!!

本文目录

  • 引言
  • 正文
    • 常见的动态内存管理错误
      • (1)对指针的越界访问
      • (2)free函数对非堆区的指针的释放是非法的
      • (3)使用free函数释放动态内存开辟空间的一部分
      • (4)对于同一块动态内存释放了一次以上
      • (5)内存泄漏(未使用free释放不再使用的动态内存)
    • 柔性数组
      • 一 柔性数组的定义与特性
      • 二 柔性数组的用法
      • 三 柔性数组的大小计算
      • 三 柔性数组的优势与应用场景
      • 四 注意事项与常见问题
      • 五 总结与展望
  • 快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!!

引言

本文是小编承接上一篇——《重生之我在异世界学编程之C语言:深入动态内存管理篇》所作的收尾和补充。逻辑上存在一定的先后顺序,建议先搞懂之前的知识点再进行本章内容的学习。那现在宝子们就跟着小编的步伐一起进入本章知识的学习。Go!Go!Go!

在这里插入图片描述


那接下来就让我们开始遨游在知识的海洋!

正文


常见的动态内存管理错误

温馨提示:

  • 本节内容所展示的代码都为错误代码,宝子们可以根据提示进行自查。

(1)对指针的越界访问

常见于:对于malloc函数的参数理解错误

例:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>int main(){int* p = (int*)malloc(100);if (p == NULL) {perror("malloc");}for (int i = 0; i < 100; i++) {printf("%d ", p[i]);}free(p);p = NULL;return 0;
}

(2)free函数对非堆区的指针的释放是非法的

常见于:对于free函数理解错误

例:

#include<stdio.h>
int main() {int arr[10] = { 0 };int* p = arr;free(p);p = NULL;
}

(3)使用free函数释放动态内存开辟空间的一部分

常见于:对于free函数理解错误

例:

#include<stdio.h>
#include<stdlib.h>
int main() {int* p = (int*)malloc(10 * sizeof(int));if (p == NULL){perror("malloc");return 0;}	p++;free(p);p = NULL;return 0;
}

(4)对于同一块动态内存释放了一次以上

常见于:对于free函数理解错误

例:

#include<stdio.h>
int main(){int* p = (int*)malloc(10 * sizeof(int));int i = 0;for (i = 0; i < 10; i++) {*(p + i) = i;}free(p);//.....free(p);p = NULL;return 0;
}

(5)内存泄漏(未使用free释放不再使用的动态内存)

常见于:对于free函数不会运用

例:

void test() {int* p = (int*)malloc(10 * sizeof(int));int i = 0;for (i = 0; i < 10; i++) {*(p + i) = i;}return p;
}
#include<stdio.h>
int main() {int* p = (int*)malloc(10 * sizeof(int));int i = 0;for (i = 0; i < 10; i++) {*(p + i) = i;}//未使用free释放p指向的内存会导致内存的泄露p = NULL;return 0;//吃内存的程序while (1) {void* p = malloc(1);//未使用free释放p指向的内存会导致内存的泄露}}

在C语言编程中,结构体(struct)是一种非常重要的数据类型,它允许我们将多个不同类型的数据组合在一起,形成一个复合类型。然而,在某些情况下,我们可能希望结构体的某个成员能够具有可变的大小,以适应不同的数据需求。为了满足这一需求,C99标准引入了柔性数组的概念

柔性数组

一 柔性数组的定义与特性

1. 定义

  • 柔性数组是结构体中的一个特殊成员,它的类型是未指定大小的数组。换句话说,这个数组在定义时没有给出具体的长度,而是留待后续使用时动态确定这种设计使得结构体可以灵活地适应不同大小的数据块

2. 特性

  • 灵活性:柔性数组允许结构体根据实际需要动态调整大小,从而提高了代码的灵活性和可重用性
  • 内存连续性:由于柔性数组紧跟在结构体的其他成员之后,因此可以保证数据的内存连续性,这对于某些需要连续访问数据的算法来说是非常有利的
  • 限制:柔性数组必须是结构体的最后一个成员,且只能有一个柔性数组成员。这是因为编译器需要在编译时知道结构体的大小,以便进行内存分配和访问。如果柔性数组不是最后一个成员或者存在多个柔性数组成员,那么编译器将无法确定结构体的大小

二 柔性数组的用法

1. 基本用法

下面是一个简单的例子,展示了如何使用柔性数组来创建一个可变大小的结构体:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef struct {int id;char name[20];// 柔性数组,用于存储可变长度的数据char data[];
} MyStruct;MyStruct* create_mystruct(const char* name, const char* data) {size_t datalen = strlen(data) + 1; // 包括终止符'\0'size_t structsize = sizeof(MyStruct) - sizeof(char) + datalen; // 计算结构体总大小// 使用malloc分配内存MyStruct* p = (MyStruct*)malloc(structsize);if (!p) {perror("Failed to allocate memory");exit(EXIT_FAILURE);}p->id = 1; // 设置IDstrncpy(p->name, name, sizeof(p->name)); // 复制名称到结构体中strncpy(p->data, data, datalen); // 复制数据到柔性数组中return p;
}void free_mystruct(MyStruct* p) {free(p);
}int main() {const char* name = "Alice";const char* data = "This is a test string.";MyStruct* s = create_mystruct(name, data);printf("ID: %d
", s->id);printf("Name: %s
", s->name);printf("Data: %s
", s->data);free_mystruct(s);return 0;
}
  • 在这个例子中,MyStruct结构体包含了一个柔性数组data,用于存储可变长度的字符串数据。create_mystruct函数根据输入的名称和数据计算结构体所需的总大小,并使用malloc函数分配相应的内存空间。然后,它将输入的数据复制到结构体中并返回指向该结构体的指针。最后,在main函数中,我们创建了一个MyStruct实例并打印出其内容。使用完毕后,通过调用free_mystruct函数释放分配的内存。

2.与指针的比较

虽然柔性数组看起来类似于在结构体中使用字符指针来指向动态分配的数据块,但它们之间有一些重要的区别:

  • 内存连续性:如前所述,柔性数组保证了数据的内存连续性,而指针则不一定。当使用指针时,数据块可能位于内存的任何位置,这可能导致缓存未命中率的增加和性能下降
  • 内存管理使用柔性数组时,整个结构体(包括柔性数组部分)通常是通过单个malloc或类似函数一次性分配的。这意味着我们可以更容易地管理内存,因为只需要一个free调用就可以释放整个结构体。相比之下,如果使用指针来指向动态分配的数据块,则需要分别管理这些块的内存分配和释放

例:

柔性数组实现柔性

typedef struct stu {int a;char arr[0];    //char arr[];   这两种写法都是C99语法规定内的,编译器会支持至少一种写法
}stu;
int main() {stu* p = (stu*)malloc(4 * sizeof(int) + 10 * sizeof(char));if (p == NULL) {perror("malloc::");return 1;}//使用int i = 0;//赋值for (i = 0; i < 10; i++) {(p->arr)[i] = i;}//打印for (i = 0; i < 10; i++) {printf("%d ", p->arr[i]);}//释放内存free(p);p = NULL;
}

非柔性数组实现柔性

typedef struct stu {int a;//char arr[0];    char* arr;}stu;int main() {//第一次申请动态内存stu* p = (stu*)malloc(sizeof(int));if (p == NULL) {perror("malloc::");return 1;}p->a = 100;//第二次申请动态内存char* pa = (char*)realloc(NULL, 10);if (pa == NULL) {perror("realloc->arr");return 1;}p->arr = pa;pa = NULL;//使用int i = 0;//赋值for (i = 0; i < 10; i++) {p->arr[i] = 'a';}//打印for (i = 0; i < 10; i++) {printf("%c ", p->arr[i]);}//第一次释放内存free(p->arr);p->arr = NULL;//第二次释放内存free(p);p = NULL;}

所以我们通过对比可以得出使用柔性数组的好处有:

  • 1.方便释放内存。
  • 2.减少系统碎片,加快访问速度。

三 柔性数组的大小计算

基本规则

  • 计算含有柔性数组(成员)的结构体的大小不包括柔性数组成员的大小,所以含有柔性数组(成员)的结构体至少有除了柔性数组外的1个成员

例:

typedef struct stu {int a;char arr[0];    //char arr[];   这两种写法都是C99语法规定内的,编译器会支持至少一种写法
}stu;
int main() {printf("%zd\n", sizeof(stu));   
} 

运行结果:

在这里插入图片描述

由此我们就能验证以上规则的正确性。


三 柔性数组的优势与应用场景

1. 优势

  • 简化内存管理:通过使用柔性数组,我们可以将数据和结构 体一起分配和管理,从而简化了内存管理的复杂性。
  • 提高性能:由于柔性数组保证了数据的内存连续性,因此可以提高某些算法的性能。例如,在处理需要连续访问数据的操作时(如字符串处理、图像处理等),使用柔性数组可以减少缓存未命中率并提高访问速度
  • 增强代码可读性:通过将数据和结构体结合在一起,我们可以使代码更加清晰易懂。这有助于维护和理解复杂的程序结构。

2. 应用场景

柔性数组在许多领域都有广泛的应用,包括但不限于以下几个方面:

  • 网络通信在网络通信中,数据包的大小通常是可变的。通过使用柔性数组,我们可以轻松地创建可变大小的数据包结构体来处理不同类型的网络消息
  • 文件处理在处理文件时,文件的内容大小和格式可能是未知的或可变的。柔性数组可以用于创建可变大小的文件头或记录结构体来读取和处理文件中的数据
  • 图像处理在图像处理中,图像的大小和分辨率可能是不同的。通过使用柔性数组,我们可以创建可变大小的图像数据结构来存储和处理不同尺寸的图像
  • 数据库操作在数据库操作中,记录的长度和内容可能是变化的。柔性数组可以用于创建可变大小的记录结构体来存储和操作数据库中的数据

四 注意事项与常见问题

1. 注意事项

  • 确保柔性数组是最后一个成员:如前所述,柔性数组必须是结构体的最后一个成员。否则,编译器将无法确定结构体的大小并进行正确的内存分配。
  • 避免多次分配内存:在使用柔性数组时,应尽量避免对结构体进行多次内存分配。最好是一次性分配足够的内存来容纳所有成员(包括柔性数组部分)。这样可以减少内存碎片和提高性能。
  • 正确处理内存释放:在使用完柔性数组后,应确保正确释放分配的内存以避免内存泄漏。这通常是通过调用free函数来实现的。但是需要注意的是,如果结构体中还包含了其他动态分配的资源(如指针指向的字符串或其他数据结构),则需要分别释放这些资源

2. 常见问题

  • 编译器支持问题:尽管柔性数组是C99标准的一部分,但并不是所有的编译器都完全支持这一特性。因此,在使用之前请检查您的编译器是否支持柔性数组以及相关的语法和功能。
  • 跨平台兼容性:由于不同平台和编译器之间的实现差异可能会导致柔性数组的行为有所不同。因此,在进行跨平台开发时请谨慎使用柔性数组并确保在不同平台上进行测试和验证。
  • 安全性问题:在使用柔性数组时需要注意安全性问题。例如,在将数据复制到柔性数组中时要确保不会超出分配的内存范围导致缓冲区溢出等问题。此外还需要注意防止内存泄漏和其他常见的安全问题。

五 总结与展望

  • 柔性数组是C语言中一个非常有用的特性,它允许开发者创建可变大小的结构体以适应不同的数据需求。通过使用柔性数组,我们可以简化内存管理、提高性能和增强代码可读性。然而,在使用柔性数组时也需要注意一些问题和限制,如确保它是结构体的最后一个成员、避免多次分配内存以及正确处理内存释放等。随着技术的不断发展,未来可能会有更多的优化和改进来提高柔性数组的性能和可靠性。同时我们也期待看到更多创新的应用场景出现以充分利用这一强大的特性。

快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!!

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

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

相关文章

无人直播源码

DY无人直播系统架构设计介绍 在DY等短视频平台的直播中&#xff0c;无人直播系统能够提供自动化、智能化的互动体验&#xff0c;既减轻了主播的工作量&#xff0c;又提升了观众的参与感。以下是一个典型的无人直播系统架构设计&#xff0c;包含全局配置、点对点互动、产品话术、…

被裁20240927 --- 嵌入式硬件开发 前篇

前篇主要介绍一些相关的概念&#xff0c;用于常识扫盲&#xff0c;后篇开始上干货&#xff01; 他捧着一只碗吃过百家的饭 1. 处理器芯片1.1 处理器芯片制造商一、 英特尔&#xff08;Intel&#xff09;二、 三星&#xff08;SAMSUNG&#xff09;三、 高通&#xff08;Qualcomm…

【Linux】进程间通信 -> 匿名管道命名管道

进程间通信的目的 数据传输&#xff1a;一个进程许需要将它的数据发送给另外一个进程。资源共享&#xff1a;多个进程之间共享同样的资源。通知事件&#xff1a;一个进程需要向另一个或一组进程发送消息&#xff0c;通知它们发生了某种事件&#xff08;如进程终止时要通知父进程…

Pytorch注意力机制应用到具体网络方法(闭眼都会版)

文章目录 以YoloV4-tiny为例要加入的注意力机制代码模型中插入注意力机制 以YoloV4-tiny为例 解释一下各个部分&#xff1a; 最左边这部分为主干提取网络&#xff0c;功能为特征提取中间这边部分为FPN&#xff0c;功能是加强特征提取最后一部分为yolo head&#xff0c;功能为获…

修改el-select下拉框高度;更新:支持动态修改

文章目录 效果动态修改&#xff1a;效果代码固定高度版本动态修改高度版本&#xff08;2024-12-25 更新&#xff1a; 支持动态修改下拉框高度&#xff09; 效果 动态修改&#xff1a;效果 代码 固定高度版本 注意点&#xff1a; popper-class 尽量独一无二&#xff0c;防止影…

如何完全剔除对Eureka的依赖,报错Cannot execute request on any known server

【现象】 程序运行报错如下&#xff1a; com.netflix.discovery.shared.transport.TransportException报错Cannot execute request on any known server 【解决方案】 &#xff08;1&#xff09;在Maven工程中的pom去掉Eureka相关的引用&#xff08;注释以下部分&#xff0…

vscode写python,遇到问题:ModuleNotFoundError: No module named ‘pillow‘(已解决 避坑)

1 问题&#xff1a; ModuleNotFoundError: No module named pillow 2 原因&#xff1a; 原因1&#xff1a;安装Pillow的pip命令所处的python版本与vscode调用的python解释器版本不同。 如&#xff1a; 原因2&#xff1a;虽然用的是pillow&#xff0c;但是写代码的时候只能用…

Ashy的考研游记

文章目录 摘要12.1112.2012.21 DAY1&#xff08;政治/英语&#xff09;政治英语 12.22 DAY2&#xff08;数学/专业课&#xff09;数学专业课 结束估分 摘要 在24年的12月里&#xff0c;Ashy完成了他的考研冲刺&#xff0c;顺利的结束了他本年度的考研之旅。 在十二月里&#…

AIGC实践|AI/AR助力文旅沉浸式互动体验探索

前言&#xff1a; 本篇文章的创作灵感来源于近期热门话题——让文物“动起来”&#xff0c;各大博物馆成为新进潮流打卡地。结合之前创作的AI文旅宣传片良好的流量和反馈&#xff0c;外加最近比较感兴趣的AR互动探索&#xff0c;想尝试看看自己能不能把这些零碎的内容整合起来…

Kubernetes(k8s)离线部署DolphinScheduler3.2.2

1.环境准备 1.1 集群规划 本次安装环境为&#xff1a;3台k8s现有的postgreSql数据库zookeeper服务 1.2 下载及介绍 DolphinScheduler-3.2.2官网&#xff1a;https://dolphinscheduler.apache.org/zh-cn/docs/3.2.2 官网安装文档&#xff1a;https://dolphinscheduler.apach…

C++的侵入式链表

非侵入式链表 非侵入式链表是一种链表数据结构&#xff0c;其中每个元素&#xff08;节点&#xff09;并不需要自己包含指向前后节点的指针。链表的结构和节点的存储是分开的&#xff0c;链表容器会单独管理这些指针。 常见的非侵入式链表节点可以由以下所示&#xff0c;即&a…

在vscode的ESP-IDF中使用自定义组件

以hello-world为例&#xff0c;演示步骤和注意事项 1、新建ESP-IDF项目 选择模板 从hello-world模板创建 2、打开项目 3、编译结果没错 正在执行任务: /home/azhu/.espressif/python_env/idf5.1_py3.10_env/bin/python /home/azhu/esp/v5.1/esp-idf/tools/idf_size.py /home…

2025差旅平台怎么选?一体化、全流程降本案例解析

差旅支出在企业中一直是一项重要但容易被忽视的成本开支&#xff0c;尤其是在项目驱动型企业中&#xff0c;因频繁的差旅需求&#xff0c;支出规模往往持续增长。以差旅平台分贝通签约伙伴——某智能制造业的业务模式为例&#xff0c;该模式要求员工定期前往不同的工厂、供应商…

【linux】NFS实验

NFS NFS服务 nfs,最早是Sun这家公司所发展出来的,它最大的功能就是可以透过网络,让不同的机器,不同的操作系统,进行实现文档的共享。所以你可以简单的将他看做是文件服务器。 实验准备 ①先准备一个服务器端的操作系统和客户端的操作系统(Red Hat)。 ②选择NAT模式,…

智源研究院与安谋科技达成战略合作,共建开源AI“芯”生态

12月25日&#xff0c;智源研究院与安谋科技&#xff08;中国&#xff09;有限公司&#xff08;以下简称“安谋科技”&#xff09;与正式签署战略合作协议&#xff0c;双方将面向多元AI芯片领域开展算子库优化与适配、编译器与工具链支持、生态系统建设与推广等一系列深入合作&a…

ROG NUC:强大内核激发创意,AI赋能学子科技探索

有这么一款能够激发无限创意、助力科技探索的迷你主机&#xff0c;它以其卓越的性能和迷你的身材成为了成为了ProArt百校行活动中的明星产品&#xff0c;助力广大学子勇敢探索未知&#xff0c;追逐属于自己的科技梦想。它就是ROG NUC 2024&#xff01; 强大性能&#xff0c;创意…

从零玩转CanMV-K230(8)-多线程例程

文章目录 前言一、_thread模块API二、使用示例创建并启动线程停止线程_thread.exit() 总结 前言 K230上不支持threading&#xff0c;只能支持_thread&#xff0c;该模块实现了相应 CPython 模块的子集&#xff0c;CPython 是 Python 编程的参考实现 语言&#xff0c;也是最著名…

yii2 手动添加 phpoffice\phpexcel

1.下载地址&#xff1a;https://github.com/PHPOffice/PHPExcel 2.解压并修改文件名为phpexcel 在yii项目的vendor目录下创建一个文件夹命名为phpoffice 把phpexcel目录放到phpoffic文件夹下 查看vendor\phpoffice\phpexcel目录下会看到这些文件 3.到vendor\composer目录下…

数据库-用户管理

一、创建用户 create user xy104192..168.42.24 identified by 123456;xy104&#xff1a;用户名 localhost&#xff1b;这个权限最高的root用户 %&#xff1a;任务ip地址 192.168.42.24&#xff1a;登录的IP地址 identified by ‘123456’&#xff1a;指定该用户的密码 mysql…

数据库安全-redisCouchdb

1.redis未授权访问 默认端口:6379 1.1 Redis沙盒逃逸漏洞RCE-CVE-2022-0543 介绍&#xff1a;Redis 是一套开源的使用 ANSI C编写、支持网络、可基于内存亦可持久化的日志型、键值存储数据库&#xff0c;并提供多种语言的API。Redis 如果在没有开启认证的情况下&#xff0c;…