【C语言】你不知道的知识小盲区——柔性数组

在这里插入图片描述

文章目录

  • 一、什么是柔性数组
  • 二、柔性数组的特点
  • 三、柔性数组的使用
  • 四、柔性数组的优势

一、什么是柔性数组

   也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。在C99标准中,如果结构体的最后一个成员是数组,那么这个数组可以不指定大小,它的大小是未知的,被称为柔性数组
   例如:

struct Stu
{int i;int arr[0];
};

   这种写法有些编译器可能会报错,可以使用这种形式:

struct Stu
{int i;int arr[];
};

   这两种写法都可以创建柔性数组,具体看编译器的选择

二、柔性数组的特点

  1. 结构中的柔性数组成员前⾯必须⾄少⼀个其他成员,比如我们上面的例子中,在柔性数组前都建立了另一个整型成员在前面
  2. sizeof返回的这种结构大小不包括柔性数组的内存,只包含其它成员的大小,我们可以来试一下计算上面的结构体Stu:
    在这里插入图片描述
       可以看到,sizeof(struct Stu)只包含了结构体中的整型成员i的大小,并没有包含柔性数组进去
  3. 包含柔性数组成员的结构体使用动态内存开辟函数进行内存的动态分配,并且分配的内存应该⼤于结构的大小,以适应柔性数组的预期大小(如果还没有学习过动态内存分配可以参考文章:【C语言】动态内存管理及相关笔试题)

   我们在给柔性数组申请空间时,一般会结合柔性数组第2和第3条特点来进行,第2点说使用sizeof计算结构体大小时不会计算柔性数组的大小,而第3点则说开辟空间时,需要大于计算出来的结构体的大小,所以我们开辟空间时的大小可以写成:sizeof(struct Stu) + 柔性数组的空间,如下:

//给结构体改名:
typedef struct Stu Stu
//为带有柔性数组的结构体Stu开辟空间
Stu* p = (Stu*)malloc(sizeof(Stu) + 10 * sizeof(int));

   以上操作就是为带有柔性数组的结构体Stu开辟空间,给除了柔性数组外的其它成员开辟空间我们采用的就是sizeof(Stu),然后再加上柔性数组的大小,上述代码中就为柔性数组开辟了10个整型的空间

三、柔性数组的使用

   刚刚我们学会了给包含柔性数组的结构体初始化,接着我们就举一个例子,就是给柔性数组开辟指定的空间,然后使用它,如下结构体:

typedef struct Stu
{int i;int arr[]; 
}Stu;

   上面的代码中,我们创建了一个带有柔性数组的结构体,将其重命名为了Stu,由于我们要指定空间内容,所以我们可以把其中的i用来表示柔性数组arr的元素个数
   但是我们要注意一点,不能直接来创建空间,因为我们的成员i需要用户输入,但是最开始的时候结构体的空间都还没有申请,成员i自然也就没有自己的空间,也就不能给i输入值
   本质上就是i不能输入,不知道柔性数组arr的元素个数,我们给结构体申请空间时就不能正常给柔性数组开辟空间,那么该怎么办呢?
   第一步就是给结构体申请不包含柔性数组的空间,因为柔性数组的元素个数还不确定,这时我们就给i申请到空间了,这时就可以使用scanf为i输入值了,如下:

typedef struct Stu
{int i;int arr[]; 
}Stu;int main()
{Stu* p = (Stu*)malloc(sizeof(Stu));scanf("%d", &p->i);return 0;
}

   这时我们就知道了用户输入的i了,也就是知道数组元素的个数,那么我们就可以为柔性数组arr开辟空间了,由于结构体Stu的空间已经被开辟了,只是没有开辟柔性数组的空间
   所以我们就可以使用realloc对原空间进行增容,再次提醒一下,不能直接用原来的指针p来接收,因为一旦realloc开辟空间失败返回了空指针,那么我们就找不到原数据了
   所以我们这里重新创建一个变量tmp来接收,然后判断它是否为空,不为空再传给我们的p,如果为空那么就直接打印错误信息,然后返回,如下:

#include <stdio.h>
#include <stdlib.h>typedef struct Stu
{int i;int arr[];
}Stu;int main()
{Stu* p = (Stu*)malloc(sizeof(Stu));scanf("%d", &p->i);Stu* tmp = (Stu*)realloc(p, sizeof(Stu) + p->i * sizeof(int));if (tmp == NULL){perror("realloc");return 1;}p = tmp;return 0;
}

   现在我们已经为柔性数组开辟了空间,现在就该使用这段空间了,我们遍历这个柔性数组,然后给它填充值,当然,使用完后也一定要记得将空间手动释放,然后将其置为空指针,以免造成内存泄漏和出现野指针,如下:

#include <stdio.h>
#include <stdlib.h>typedef struct Stu
{int i;int arr[];
}Stu;int main()
{Stu* p = (Stu*)malloc(sizeof(Stu));scanf("%d", &p->i);Stu* tmp = (Stu*)realloc(p, sizeof(Stu) + p->i * sizeof(int));if (tmp == NULL){perror("realloc");return 1;}p = tmp;for (int j = 0; j < p->i; j++){p->arr[j] = j;printf("%d ", p->arr[j]);}free(p);p = NULL;return 0;
}

   我们来看看运行结果:
在这里插入图片描述
   可以看到我们已经实现了我们的需求,根据输入的值来确定柔性数组元素个数,从而创建了一个柔性数组,随后进行使用,这就是我们柔性数组的基本用法
   我们扩散一下思维,这里的柔性数组是不是很像我们学过的一个东西,每错,就是变长数组,根据输入的值来确定数组的元素个数,这不就是变长数组吗?我们也说过VS默认不支持变成数组,那么我们就可以使用柔性数组,虽然麻烦一点,但是至少可以用了
   当然还有另一个办法,就是给VS加上clang组件,然后在项目菜单,选择属性,在常规中将平台工作集改成clang,流程图如下:
在这里插入图片描述

四、柔性数组的优势

   实际上上面的那些关于柔性数组的操作,也可以不使用柔性数组完成,那么我们为什么还要引入柔性数组呢?我们接下来不使用柔性数组来完成以上操作来和柔性数组做一个对比就知道了
   具体是什么方法呢?实际上也不陌生,就是我们上一篇在动态内存管理中讲过的malloc模拟实现数组的功能的办法,使用一个整型指针来当作一个数组的首元素,然后给它开辟空间,把这段连续空间当作数组使用,首先创建如下结构体:

typedef struct Stu
{int i;int* arr;
}Stu;

   接着我们就使用动态内存管理的malloc来为结构体指针Stu开辟空间,接着还是使用scanf让用户输入一个i,代表数组元素个数,然后根据i的大小来为这个数组开辟相应的空间,如下:

	Stu* p = (Stu*)malloc(sizeof(Stu));scanf("%d", &p->i);int* tmp = (int*)malloc(p->i * sizeof(int));if (tmp == NULL){perror("malloc");return 1;}p->arr = tmp;

   现在我们给这个数组申请了空间,就可以使用了,然后对它进行释放,如下:

#include <stdio.h>
#include <stdlib.h>typedef struct Stu
{int i;int* arr;
}Stu;int main()
{Stu* p = (Stu*)malloc(sizeof(Stu));scanf("%d", &p->i);int* tmp = (int*)malloc(p->i * sizeof(int));if (tmp == NULL){perror("malloc");return 1;}p->arr = tmp;for (int j = 0; j < p->i; j++){p->arr[j] = j;printf("%d ", p->arr[j]);}free(p);p = NULL;return 0;
}

   那么这段代码有没有问题?其实是有问题的,因为我们这里使用了两个malloc,一个给了结构体开辟空间,另一个为结构体中的arr开辟了空间,而两次malloc开辟的空间不一定是连续的,所以我们释放时只释放了结构体的空间就不够
   我们还需要先释放开辟的数组的空间,把它置为空指针,再释放掉p,将p置为空指针,所以 最后真正完整的代码如下:

#include <stdio.h>
#include <stdlib.h>typedef struct Stu
{int i;int* arr;
}Stu;int main()
{Stu* p = (Stu*)malloc(sizeof(Stu));scanf("%d", &p->i);int* tmp = (int*)malloc(p->i * sizeof(int));if (tmp == NULL){perror("malloc");return 1;}p->arr = tmp;for (int j = 0; j < p->i; j++){p->arr[j] = j;printf("%d ", p->arr[j]);}free(p->arr);p->arr = NULL;free(p);p = NULL;return 0;
}

我们来看看运行结果:
在这里插入图片描述

   可以我们就使用这种方式就模拟了柔性数组的用法,随后我们可以对比一下它们,其中柔性数组有以下两个优势:

  1. 第⼀个好处是:使用柔性数组方便内存释放
       如果我们的代码是在⼀个给别人用的函数中,你里面做了⼆次内存分配,并把整个结构体返回给用户,用户调⽤free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事
       所以,如果我们把结构体的内存以及其成员要的内存⼀次性分配好了,并返回给用户⼀个结构体指针,用户做⼀次free就可以把所有的内存也给释放掉,否则用户可以忘记释放数组的空间,造成内存泄漏
  2. 第⼆个好处是:使用柔性数组有利于访问速度
       柔性数组的空间是连续的,而我们上面的那种方式空间就不是连续的,而连续的内存有益于提⾼访问速度,也有益于减少内存碎⽚(其实,我个⼈觉得也没多⾼了,反正你跑不了要⽤做偏移量的加法来寻址,但是至少也算是好处之一)

   今天分享的柔性数组就到这里了,友友们之前有没有听说过柔性数组这个知识点呢?欢迎在评论区说出你们的想法
   那么我们下次再见,bye~

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

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

相关文章

Spring Integration + MQTT

1. 简介 Spring Integration&#xff1a; Spring Integration是一个开源的Java库&#xff0c;用于构建基于消息的应用程序。它提供了一套丰富的组件和工具&#xff0c;使得开发者可以轻松地开发出可靠、灵活和可扩展的集成解决方案。以下是Spring Integration的一些主要用途&…

jenkins 插件Publish Over SSH

一、安装插件 二、配置sshserver http://192.168.31.156:8080/manage/configure 三、添加自由风格&#xff1a;PublishOverSSHDemo 我们将工作目录&#xff1a;/var/lib/jenkins/workspace/PublishOverSSHDemo下的图片m3.jpeg 同步到目标143服务器目录&#xff1a;/root/imag…

SQL分类中的DDL

DDL&#xff08;Data Definition Language):数据定义语言&#xff0c;用来定义数据库对象&#xff08;数据库&#xff0c;表&#xff0c;字段&#xff09;。 一、DDL语句操作数据库 1、查询所有数据库&#xff1a;show databases&#xff1b;&#xff08;一般用大写&#xff…

OpenCV-人脸检测

文章目录 一、人脸检测流程二、关键方法三、代码示例四、注意事项 OpenCV是一个开源的计算机视觉和机器学习软件库&#xff0c;它提供了多种人脸检测方法&#xff0c;以下是对OpenCV人脸检测的详细介绍&#xff1a; 一、人脸检测流程 人脸检测是识别图像中人脸位置的过程&…

【Docker系列】Docker查看镜像架构

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

模态与非模态的对话框

本文学习自&#xff1a; 《Qt Creato快速入门》 #include "widget.h" #include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); }1. #include "widget.h" #include "ui_w…

MySQL数据的导入

【图书推荐】《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;》-CSDN博客 《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;&#xff08;数据库技术丛书&#xff09;》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…

小白也能学会的预测新模型!ReliefF特征选择+XGBoost回归!

小白也能学会的预测新模型&#xff01;ReliefF特征选择XGBoost回归&#xff01; 目录 小白也能学会的预测新模型&#xff01;ReliefF特征选择XGBoost回归&#xff01;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab实现ReliefF-XGBoost多变量回归预测 1.excel数据…

有问必答!zabbix“专家坐诊”第259期问答

问题一 Q&#xff1a;现在监控项4万多&#xff0c;调整到多少比较合理 zabbix7.03&#xff1f; A&#xff1a;慢慢往上调&#xff0c;没有标准。 问题二 Q&#xff1a;想问下大家&#xff0c;zabbix的监控项怎么不能自动清除&#xff0c;比如说这次监控是A监控项&#xff0c;下…

如何通过构建对应的api服务器使Vue连接到数据库

一、安装数据库驱动 在后端安装 MySQL 数据库驱动&#xff0c;比如在 Node.js 环境中可以使用 mysql2 包来连接 MySQL 数据库。在项目目录下运行以下命令安装&#xff1a; npm install mysql2或者使用 yarn&#xff1a; yarn add mysql2二、创建数据库连接模块 创建一个专门…

Light灯光组件+组件的相关操作+游戏资源的加载

Light灯光组件 Type: Directional:平行光&#xff0c;模仿的是太阳光 Spot:聚光灯 Area:区域光 Color&#xff1a; 颜色值 Mode: RealTime:实时 Mix:混合 Baked:烘焙 Intersity: 光照强度 Indirect Multiplier:光照强度乘数 Shadow Type:影子设置&#xff1a;…

Java通过RAG构建专属知识问答机器人_超详细

RAG&#xff1a;融合检索与生成的文本精准生成技术 检索增强生成&#xff08;RAG&#xff09;是一种技术&#xff0c;它通过结合检索模型和生成模型来提高文本生成的准确性。具体来说&#xff0c;RAG首先利用检索模型从私有或专有的数据源中搜索相关信息&#xff0c;然后将这些…

智能优化算法-水循环优化算法(WCA)(附源码)

目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1.内容介绍 水循环优化算法 (Water Cycle Algorithm, WCA) 是一种基于自然界水循环过程的元启发式优化算法&#xff0c;由Shah-Hosseini于2012年提出。WCA通过模拟水滴在河流、湖泊和海洋中的流动过程&#xff0c;以及蒸发…

【load_file读文件】

一、文件操作基础 show 先试试 show variables;发现显示了三百多行的系统变量: 这是数据库的目录&#xff1a; mysql有多种编码方式&#xff0c;有数据库编码、连接时的编码、还有客户端的编码&#xff1a; 这里还有一个日志路径&#xff0c;这个日志是需要手动打开的&#…

CSMA/CA协议

802.11局域网在使用CSMA/CA的同时&#xff0c;还使用确认重传&#xff08;ARQ&#xff09;。这是因为无线信道的通信质量远不如有线信道的&#xff0c;因此无线站点每通过无线局域网发送完一帧后&#xff0c;要等到收到对方的确认帧后才能继续发送下一帧。这就是链路层确认。 帧…

C语言笔记 12

逻辑类型 bool&#xff1a;在“#include <stdbool.h>”之后就可以使用bool和true、false 并没有真正的bool量的类型 逻辑运算 逻辑运算是对逻辑量进行的运算&#xff0c;结果只有0或1逻辑量是关系运算或逻辑运算的结果 运算符描述示例结果!逻辑非!a如果a是true结果就是…

ARP欺骗的多种手法

学习参考&#xff1a; ARP欺骗的各种d玩法-CSDN博客 https://juejin.cn/post/7383702153892954164 一、什么是ARP欺骗 1.什么是ARP&#xff1f; ARP (Address Resolution Protocol) 是一种网络层协议&#xff0c;用于将 IP 地址转换为物理地址&#xff08;MAC 地址&#xff0…

paddlepaddle显存未正常释放

NVIDIA GPU 显存未正常释放 问题描述 paddlepaddle 训练过程出现问题中断等导致GPU显存没有释放。 情况1: 使用nvidia-smi -l查看显存占用情况&#xff0c;输出结果中没有显示PID,但是有显存占用。 解决方法 使用killall python 直接kill掉所有python进程。假如运行此命…

LINUX——内核移植、内核编译教程

Linux内核编译是一个将内核源代码转换成可在特定硬件架构上运行的二进制文件的过程。以下是编译Linux内核的一般步骤&#xff1a; 1、准备工作&#xff1a; 确保安装了必要的编译工具&#xff0c;如gcc、make、ncurses库&#xff08;用于make menuconfig&#xff09;等。 2、…

点云深度学习系列:4DenoiseNet——考虑时空维度的去雪模型

文章&#xff1a;4DenoiseNet: Adverse Weather Denoising From Adjacent Point Clouds 代码&#xff1a;https://github.com/alvariseppanen/4DenoiseNet 1&#xff09;摘要 可靠的点云数据对于感知任务至关重要&#xff0c;例如在机器人和自动驾驶应用中。恶劣天气会导致特定…