【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.rhkb.cn/news/447985.html

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

相关文章

sqli-labs less-26 空格绕过

空格绕过 过滤空格 用Tab代替空格%20 %09 %0a %0b %0c %0d %a0 //() 绕过空格注释符绕过//–%20//#–- -;%00; 空白字符绕过SQLite3 —— 0A,0D,0c,09,20 MYSQL 09,0A,0B,0B,0D,A0,20 PosgressSQL 0A,0D,0C,09,20 Oracle_11g 00,0A,0D,0C,09,20 MSSQL 01,02,03,04,05,06,07,…

[瑞吉外卖]-05菜品模块

文件上传下载 介绍 文件上传也称为upload&#xff0c;是指将本地图片、视频、音频等文件上传到服务器上, 可以供其他用户浏览或下载 前端组件库提供了上传组件&#xff0c;但是底层原理还是基于form表单的文件上传。 服务端要接收客户端上传的文件&#xff0c;通常都会使用Ap…

一次Fegin CPU占用过高导致的事故

记录一下 一次应用事故分析、排查、处理 背景介绍 9号上午收到CPU告警&#xff0c;同时业务反馈依赖该服务的上游服务接口响应耗时太长 应用告警-CPU使用率 告警变更 【WARNING】项目XXX,集群qd-aliyun,分区bbbb-prod,应用customer,实例customer-6fb6448688-m47jz, POD实例CP…

Web集群服务-Nginx

1. web服务 1. WEB服务:网站服务,部署并启动了这个服务,你就可以搭建一个网站 2. WEB中间件: 等同于WEB服务 3. 中间件:范围更加广泛,指的负载均衡之后的服务 4. 数据库中间件:数据库缓存,消息对列 2. 极速上手指南 nginx官网: nginx documentation 2.1 配置yum源 vim /etc/…

HTML基础知识

介绍 HTML&#xff08;HyperText Markup Language&#xff0c;超文本标记语言&#xff09;是一种用于创建网页的标准标记语言。它描述了一个网站的结构骨架&#xff0c;使得浏览器能够展示具有特定格式的文本、链接、图片和其他内容。以下是HTML的一些基础知识&#xff1a; HT…

骨传导耳机哪个牌子好?自费测评5大爆款骨传导耳机,高能不断!

随着科技的飞速发展&#xff0c;耳机市场也迎来了一次又一次的革新。从有线到无线&#xff0c;从入耳式到头戴式&#xff0c;每一次技术的突破都为用户带来了全新的听觉体验。近年来&#xff0c;骨传导耳机以其独特的传声方式和健康舒适的佩戴体验&#xff0c;逐渐成为运动爱好…

初识Linux之指令(二)

一&#xff1a;head指令 head 与 tail 就像它的名字一样的浅显易懂&#xff0c;它是用来显示开头或结尾某个数量的文字区块&#xff0c;head 用来显示档案的 开头至标准输出中&#xff0c;而 tail 想当然尔就是看档案的结尾。 语法&#xff1a;head 【参数】 【文件】 功能&…

docker (desktopcompose) download

docker docker-compose download 百度网盘获取离线包链接release-notes 参考dockerdocker-composewlspowershell

Loss:Objects as Points

目录 3. 预备知识4. 物体作为点4.1. 3D 检测4.2. 人体姿态估计4-(1). 物体作为点的核心概念4-(2). 从点到边界框的推理过程4-(3). 3D 检测4-(4). 人体姿态估计5. 实现细节目录 3. 预备知识4. 物体作为点4.1. 3D 检测4.2. 人体姿态估计4-(1). 物体作为点的核心概念4-(2). 从点到…

微知-Mellanox提供的一个不错的测试rdma_cm方式建链的工具软件ucmatose?(ucmatose; ucmatose -s 1.1.1.1)

文章目录 快速命令获取背景实验server端客户端一个错误的情况无法建链&#xff1a; rpm安装包&#xff1a;librdmacm-utils-48.0-1.0.1.an8.x86_64详细介绍综述 快速命令获取 #server端 ucmatose# client端 ucmatose -s 1.1.1.1背景 平时使用rdma cm建链的测试一般使用ib_wri…

算法:974.和可以被K整除的子数组

题目 链接:leetcode链接 思路分析&#xff08;前缀和 同余定理&#xff09; 首先&#xff0c;我们要了解一下什么是同余定理 同余定理&#xff1a; 如果&#xff08;a - b&#xff09;/ p k …… 0 则 a % p b % p 证明我写在草稿纸上&#xff0c;如下图&#xff1a; 初…

Ubuntu QT 交叉编译环境搭建

文章目录 下载安装qtCreatornot a valid identifier 的错误 安装g下载并安装交叉编译器下载交叉编译器安装交叉编译器 下载编译 ARM 的Qt平台源码配置arm的QT平台 下载安装qtCreator 去QT下载官网下载对应需要的QT软件。 这里下载5.12.96版本的 改变安装包权限&#xff0c;…

红海云首届粤港澳大湾区人力资源服务创新创业大赛斩获殊荣

10月11日&#xff0c;由广东省人力资源和社会保障厅、广州市人民政府主办&#xff0c;广州市人力资源和社会保障局承办的“首届粤港澳大湾区人力资源服务创新创业大赛”决赛落下帷幕&#xff0c;红海云凭借领先的HR数字化产品创新实践斩获大赛三等奖。 本次大赛以“激活人力创…

21c RAC CLSRSC-143 CLSRSC-150 crsgpnp.pm line 2063

CLSRSC-143:Failed to create a peer profile for Oracle Cluster gpnp using ‘gpnptool’(error code 1) 21c RAC GI安装时&#xff0c;在跑root.sh时报错如下&#xff1a; 查看了下crsgpnp.pm line 2063为如下原代码 第一感觉是主机名太长了&#xff0c;将主机名缩减后…

基础IO -- 理解文件(1)

目录 一&#xff1a;回顾文件 二&#xff1a;加深对文件的理解 1.概念 2.以w写方式打开 3.以a追加方式打开 4.重定向 一&#xff1a;回顾文件 以前学习过在C语言中的文件操作&#xff0c; 但那根本是不足以理解文件的&#xff0c;即站在语言角度是不可能理解文件的 我们要…

3. 单例模式唯一性问题—构造函数

1. 构造函数带来的唯一性问题指什么&#xff1f; 对于不继承MonoBehaviour的单例模式基类 我们要避免在外部 new 单例模式类对象 例如 &#xff08;完整单例模式定义在上一节&#xff09; public class Main : MonoBehaviour {void Start(){// 破坏单例模式的唯一性&#xf…

电能表预付费系统-标准传输规范(STS)(6)

6. POSToTokenCarrierInterface 应用层协议 6.1 APDU: ApplicationProtocolDataUnit 应用协议数据单元 6.1 .1 Data elements in the APDU The APDU is the data interface between the POSApplicationProcess and the application layer protocol and comprises the data e…

新装ubuntu22.04必做两件事,不然可能没法用

一、换服务源 在全部里面找到软件和安装&#xff1b;打开后 在更多里面匹配一下最适合自己的软件源&#xff1b;这个过程比较漫长&#xff1b;要耐心等待 二、换软件安装中心 先执行&#xff1a; sudo apt upgrade 后执行&#xff1a; sudo apt install plasma-discover…

Rust默认使用UTF-8编码来解析源代码文件。如果在代码中包含无法用UTF-8编码表示的字符,编译器会报错!

文章目录 Rust默认编码示例在ANSI编码下中文显示正常的代码在UTF-8编码下将显示不正常在编译时&#xff0c;Rust使用UTF-8编码来解析代码&#xff0c;发现无法用UTF-8编码表示的字符&#xff0c;于是编译器报错 Rust默认编码 Rust 语言默认使用 UTF-8 编码来解析源代码文件。如…

免费Excel工作表同类数据合并工具

下载地址&#xff1a;https://pan.quark.cn/s/81b1aeb45e4c 在 Excel 表格里&#xff0c;当我们试图手动将多行同类数据合并为一行时&#xff0c;会遭遇诸多棘手的困难以及繁杂的操作流程。在确定哪些数据属于可合并的同类数据时&#xff0c;单纯依靠人工进行对比&#xff0c;极…