C语言进阶——动态内存分配

一、为什么存在动态内存分配?

        已经掌握的内存方式:

int val = 100;
char a[100= = {0};

        上述开辟方式有两个特点:

  1. 空间开辟大小是固定的。
  2. 数组在声明的时候,必须指定数组的长度,它所需要的内存在编译时分配。

        有时候,程序所需要的内存只有在程序运行时才能知道,那数组的编译时开辟空间的方法就不能满足了,这时只能采用动态内存开辟了。

二、动态内存函数

2.1malloc函数

        C语言提供了一个动态内存开辟的函数:

void* malloc(size_t size);

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

  1. 如果开辟成功,则返回一个指向开辟好空间的指针。
  2. 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做好检查。
  3. 返回值类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候由使用者决定。
  4. 如果size = 0,malloc的行为标准是未定义的,取决于编译器。 

2.2free函数

        动态申请的内存当程序退出时,是不会主动释放的。C语言提供了另一个函数,用来回收和释放动态内存。

void free(void* ptr);

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

  1. 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。
  2. 如果参数ptr是NULL指针,则函数什么都不做。
#include <stdio.h>
#include<stdlib.h>
int main()
{int* p = (int*)malloc(40);if (p == NULL){perror("malloc");return 1;}int i = 0;for (i = 0; i < 10; ++i){printf("%d\n", *(p + i));}free(p);p = NULL;return 0;
}

        这段代码中,指针p本身在栈区,而动态开辟的空间存在于堆区,在堆区的空间被释放之后,p失去了指向的内容,所以应该主动将p置为NULL。

2.3calloc函数

        calloc函数也用来进行动态内存分配。

void* calloc(size_t num, size_t size);

        calloc函数的功能为:

  1. 函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0。
  2. 与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为全0。 

2.4realloc函数 

        realloc函数的出现让动态内存管理更加灵活。realloc函数可以做到对动态开辟的内存进行大小调整。

void realloc(void* ptr, size_t size);
  1. ptr是需要调整的内存地址。
  2. size是调整之后的新大小。
  3. 返回值为调整后的内存起始地址。
  4. 这个函数在调整原函数内存空间大小的基础之上,还会将原来内存中的数据转移到新的空间。
  5. 调整内存空间时有两种情况:
    1. 原有空间之后有足够大的空间,那就直接在原有内存之后追加空间,原来空间的数据不发生变化。
    2. 原有空间之后没有足够多的空间,则在堆空间上另找一个合适大小的连续空间来使用,这样函数返回的是一个新的内存地址,旧的空间被释放。

3.常见的动态内存错误

3.1对NULL指针的解引用操作

void test()
{int* p = (int*)malloc(INT_MAX / 4);*p = 20;//如果p的值是NULL,就会有问题free(p);
}

        在对动态开辟的空间进行操作时,尽量进行空指针检测。

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

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

        访问超出已分配内存范围的数据会导致未定义行为,这意味着程序的行为是不可预测的,可能看似正常运行,但实际上存在隐患。 

3.3对非动态开辟内存使用free函数释放 

void test()
{int a = 10;int* p = &a;free(p);//ok?
}

         对非动态开辟的内存使用free函数会导致未定义行为,甚至可能影响到内存管理库内部的状态,它可能会破坏内存管理机制,导致内存泄漏或者其他更严重的错误。

3.4使用free释放一块动态开辟内存的一部分

void test()
{int *p = (int *)malloc(100);++p;free(p);//p不再指向动态内存的起始位置
}

         如果你试图释放部分内存,整个原先分配的内存块都会被释放,而不是仅仅释放指定的部分。

3.5对同一块动态内存多次释放 

void test()
{int* p = (int*)malloc(100);free(p);free(p);//重复释放
}

         重复释放可能会导致程序崩溃、内存管理错误、数据损坏等严重问题。这种错误的影响可能是间接的,可能不会立即显现出来,但在后续的程序执行中会引起各种问题。

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

void test()
{int* p = (int*)malloc(100);if (p != NULL){*p = 20;}
}
int main()
{test();while (1);
}

         忘记释放不再使用的动态开辟的空间会导致内存泄漏。切记:动态开辟的空间一定要正确的释放。

四、C/C++内存的开辟

        内存空间可以划分为内核空间(用户代码不能读写)、栈区(向下增长)、内存映射段(文件映射、动态库、匿名映射)、堆区(向上增长)、数据段(全局变量、局部变量)以及代码段(可执行代码/只读常量)。

  1. 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时,这些存储单元被自动释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址的等。
  2. 堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。
  3. 数据段(静态区):存放全局变量、静态数据。程序结束后由系统释放。
  4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。

五、柔性数组

        在C99中,结构体中最后一个元素允许是未知大小的数组,这就叫做柔性数组成员。

typedef struct st_type
{int i;int a[0];
}type_a;typedef struct st_type
{int i;int a[];
}type_b;

柔性数组的特点: 

  1.  结构体中的柔性数组成员前面必须至少一个其他成员。
  2. sizeof返回的这种结构大小不包括柔性数组的内存。
  3. 包含柔性数组成员的结构用malloc函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
#include<stdio.h>
#include<stdlib.h>typedef struct s
{int n;int a[];
}type_a;int main()
{int i = 0;type_a* p = (type_a*)malloc(sizeof(type_a) + 100 * sizeof(int));if (p == NULL){perror("malloc");return 1;}p->n = 100;for (int i = 0; i < 100; ++i){p->a[i] = 1;}//空间不够,使用realloc进行扩容type_a* p1 = (type_a*)realloc(p, sizeof(type_a)+ 120*sizeof(int));if (p1 == NULL){perror("realloc");return 1;}p = p1;p->n = 120;for (int i = 0; i < 120; ++i){printf("%d\n", p->a[i]);}return 0;
}

         柔性数组应用场景并不是特别丰富。

 

   

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

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

相关文章

全星魅 北斗三号船载终端的优势和领域利用

QM43BS型北斗三号船载终端&#xff1a;开启航海通信与定位新时代 在当今这个信息化高速发展的时代&#xff0c;航海领域对于通信与定位技术的需求愈发迫切。深圳市全民北斗科技有限公司&#xff0c;作为北斗技术应用领域的佼佼者&#xff0c;针对数传通信和位置服务应用&#x…

Python 实现深度学习模型预测控制--预测模型构建

链接&#xff1a;深度学习模型预测控制 链接&#xff1a;WangXiaoMingo/TensorDL-MPC: DL-MPC(deep learning model predictive control) is a software toolkit developed based on the Python and TensorFlow frameworks, designed to enhance the performance of tradition…

你了解kafka消息队列么?

消息队列概述 一. 消息队列组件二. 消息队列通信模式2.1 点对点模式2.2 发布/订阅模式 三. 消息队列的优缺点3.1 消息队列的优点3.2 消息队列的缺点 四. 总结 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&…

Android Junit 单元测试 | 依赖配置和编译报错解决

问题 为什么在依赖中添加了testImplement在build APK的时候还是会报错&#xff1f;是因为没有识别到test文件夹是test源代码路径吗&#xff1f; 最常见的配置有: implementation - 所有源代码集(包括test源代码集)中都有该依赖库.testImplementation - 依赖关系仅在test源代码…

理解磁盘结构---CHS---LAB---文件系统

1&#xff0c;初步了解磁盘 机械磁盘是计算机中唯的一个机械设备&#xff0c; 特点是慢&#xff0c;容量大&#xff0c;价格便宜。 磁盘上面的光面&#xff0c;由数不清的小磁铁构成&#xff0c;我们知道磁铁是有n&#xff0f;&#xff53;极的&#xff0c;这刚好与二进制的&…

selenium脚本编写及八大元素定位方法

selenium脚本编写 上篇文章介绍了selenium环境搭建&#xff0c;搭建好之后就可以开始写代码了 基础脚本,打开一个网址 from selenium import webdriver driver webdriver.Chrome()#打开chrome浏览器 driver.get(https://www.baidu.com) #打开百度 打开本地HTML文件 上篇…

利用Kubernetes原生特性实现简单的灰度发布和蓝绿发布

部分借鉴地址: https://support.huaweicloud.com/intl/zh-cn/bestpractice-cce/cce_bestpractice_10002.html 1.原理介绍 用户通常使用无状态负载 Deployment、有状态负载 StatefulSet等Kubernetes对象来部署业务&#xff0c;每个工作负载管理一组Pod。以Deployment为例&#x…

Macos m系列芯片环境下安装python3以及mysqlclient流程以及遇到的一系列问题

最近升级了生产力&#xff0c;换了m3的mbp&#xff0c;迁移项目的时候遇到的一系列python mysqlclient的环境问题&#xff0c;这里总结记录一下。 设备&#xff1a;Macbook Pro m3系统&#xff1a;macos Sonoma 14.6最终成功的python版本&#xff1a;Python3.9.1最终系统环境下…

STL-常用容器-list

1list基本概念 **功能&#xff1a;**将数据进行链式存储 链表&#xff08;list&#xff09;是一种物理存储单元上非连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接实现的 链表的组成&#xff1a;链表由一系列结点组成 结点的组成&#xff1a;一个是存储…

基于django的志愿者社团管理系统

大家是不是常常遇到校园社团活动组织混乱、统计繁琐的困扰&#xff1f;其实我以前在做毕业设计的时候也头疼这些问题。后来&#xff0c;我们开发了一个基于Django的志愿者社团管理系统&#xff0c;可以帮你轻松解决这些麻烦&#xff01;&#x1f4cc; &#x1f680; 核心功能模…

Opensearch集群部署【docker、服务器、Helm多种部署方式】

操作系统兼容性 我们建议在 Red Hat Enterprise Linux (RHEL) 或使用systemd的基于 Debian 的 Linux 发行版上安装 OpenSearch &#xff0c;例如 CentOS、Amazon Linux 2 和 Ubuntu Long-Term Support (LTS)。OpenSearch 应该适用于大多数 Linux 发行版&#xff0c;但我们只测…

Java题集练习4

Java题集练习4 1 异常有什么用&#xff1f; 用来找到代码中产生的错误 防止运行出错2 异常在java中以什么形式存在&#xff1f; 异常在java中以类的形式存在&#xff0c;分为运行时异常和编译期异常&#xff0c;他们都在类Exception中3 异常是否可以自定义&#xff1f;如何自…

衡石分析平台系统分析人员手册-导入图表库图表

导入图表库图表​ 本文讲述在仪表盘中如何使用图表库图表&#xff0c;如果您还不了解图表库&#xff0c;请先点击链接了解它的功能和作用。 在数据集市中建立图表库后&#xff0c;分析人员可以在应用创作中引用图表库图表&#xff0c;快速的进行数据分析工作。 导入图表库图…

【建造&机械】木材运输车辆检测系统源码&数据集全套:改进yolo11-GhostHGNetV2

改进yolo11-SPPF-LSKA等200全套创新点大全&#xff1a;木材运输车辆检测系统源码&#xff06;数据集全套 1.图片效果展示 项目来源 人工智能促进会 2024.10.28 注意&#xff1a;由于项目一直在更新迭代&#xff0c;上面“1.图片效果展示”和“2.视频效果展示”展示的系统图片…

ubuntu 20.04编译驱动报gcc-12 not found错误

最近在自己安装的Ubuntu 系统上编译自定义驱动&#xff0c;发现无法编译.ko,错误如下&#xff1a; 按照如下操作&#xff0c;发现可以解决&#xff0c;记录下&#xff0c;主要是Ubuntu缺少g-12的包 安装包以后发现可以正常编译

什么是微服务中的反应性扩展?

大家好&#xff0c;我是锋哥。今天分享关于【什么是微服务中的反应性扩展&#xff1f;】面试题&#xff1f;希望对大家有帮助&#xff1b; 什么是微服务中的反应性扩展&#xff1f; Reactive Extensions 也称为 Rx。这是一种设计方法&#xff0c;我们通过调用多个服务来收集结果…

电脑程序变化监控怎么设置?实时监控电脑程序变化的五大方法,手把手教会你!

​在现代办公和信息安全领域&#xff0c;实时监控电脑程序变化是一项至关重要的任务。 无论是企业内网安全、员工行为审计&#xff0c;还是个人电脑的隐私保护&#xff0c;了解并设置有效的监控方法都是必不可少的。 本文将详细介绍五种电脑程序变化监控的方法&#xff0c;帮助…

DEVOPS: 集群伸缩原理

概述 阿里云 K8S 集群的一个重要特性&#xff0c;是集群的节点可以动态的增加或减少有了这个特性&#xff0c;集群才能在计算资源不足的情况下扩容新的节点&#xff0c;同时也可以在资源利用 率降低的时候&#xff0c;释放节点以节省费用理解实现原理&#xff0c;在遇到问题的…

华为原生鸿蒙操作系统的发布有何重大意义和影响:

#1024程序员节 | 征文# 一、华为原生鸿蒙操作系统的发布对中国的意义可以从多个层面进行分析&#xff1a; 1. 技术自主创新 鸿蒙操作系统的推出标志着中国在操作系统领域的自主创新能力的提升。过去&#xff0c;中国在高端操作系统方面依赖于外国技术&#xff0c;鸿蒙的发布…

outlook创建新账户时报错2603、2604的解决办法

全新的戴尔笔记本电脑&#xff0c;自带的Win11家庭版&#xff0c;安装ms office 2021也顺利完成。 但是奇怪的是&#xff0c;只有其中一台笔记本电脑&#xff0c;OUTLOOK无法添加新账户。 但是这个账号在WEB端登录正常&#xff0c;由于是新入职的员工&#xff0c;根据以往经验&…