C语言柔型数组

news2024/10/5 22:11:55

何为柔性数组

所谓柔性数组,是C语言中的一个概念,也叫零长数组。顾名思义,这个数组的长度是不固定的,当没有值时,它的sizeof长度为0。
我们一般这样定义一个柔性数组:

struct buffer_t {
	int len;
	char buf[];	//柔性数组
};

上面的结构体struct buffer_t中的char buf[]就是一个柔性数组。
那么,怎样才算满足一个柔性数组的定义呢?它至少要满足以下这几个条件:

  • 柔性数组必须要定义在结构体中
  • 柔性数组必须位于结构体的最后一个位置
  • 结构体中除了柔性数组以外,还必须有其他的成员

柔性数组如何使用

上面的struct buffer_t,如果我们对它求sizeof,其实是等于int len的长度的,也就是说 ,char buf[]在结构体里并没有占长度,这也就是零长数组说法的由来。
因此,如果我们想要定义一个1024字节的数组 ,可以这样做:

struct buffer_t  *buffer = malloc(sizeof(struct buffer_t) + 1024 * sizeof(char));

我们之所以能够这样定义,是因为柔性数组正好处于结构体的最后一个元素 。
而柔性数组的赋值和访问,这就和普通数组一样的使用就行了。

strcpy(buffer->buf, "hello world");
printf("buf: %s\n", buffer->buf);

柔性数组的好处

那么,为啥会有柔性数组这么奇怪的定义?它和传统的数组定义相比,有啥好处呢?
假设我们不用柔性数组,想要定义一个可以随时使用的字符串,可能的定义方法如下:

struct normal_buffer_t {
	char *buf;
	int len;
};

而我们需要给该数组申请空间的时候,可能需要做下面几件事:

struct normal_buffer_t *nbuffer = malloc(sizeof(struct normal_buffer_t));
nbuffer->buf = malloc(1024);

当释放的时候,则需要释放两次:

free(nbuffer->buf);
free(nbuffer);

这也就意味着,我们需要给buf单独的申请和释放内存,而对于柔性数组,就没有这个麻烦,对于C语言这样一门需要手动管理内存的编程语言来说,很可能一不留神就遗漏了一次释放,造成内存泄露。
当然这还不是最主要的,而是每多一次malloc,就会多一次系统开销,也会增加内存碎片产生,如果这样的操作很多,那么进程将会产生非常多的内存碎片,严重的情况将导致没有一块比较完整的内存以供后来的程序片段使用,从而造成coredump
对于传统的数组,它在内存里分布可能是如下的样子:
在这里插入图片描述
而柔性数组,它在内存里则是连续的:
在这里插入图片描述

场景应用

假设我们现在要定义一个字符串,该字符串可以自动扩缩容,可以非常方便地追加数据,按常规做法,我们可能有如下代码:

typedef struct buf_t {
        char    *data;  //存储数据的字符串
        int     cap;    //buf的容量
        int     size;   //字符串实际长度
}buf_t;

#define BUF_CAP_DEFAULT 1046576

//根据长度申请buf
buf_t *buf_new_size(int cap){
    buf_t *buf = calloc(1, sizeof(buf_t));
    buf->size = 0;
    buf->cap = cap;
    buf->data = (char *)calloc(1, cap);

    return buf;
}

//默认创建出1M大小的buf
buf_t *buf_create(){
    return buf_new_size(BUF_CAP_DEFAULT);
}

//内存扩容
void buf_scale_up(buf_t *buf, int cap){
    int new_cap = cap << 1;
    buf->data = realloc(buf->data, new_cap);
    buf->cap = new_cap;
}

//内存缩容
void buf_scale_down(buf_t *buf, int cap){
    int new_cap = cap >> 1;
    buf->data = realloc(buf->data, new_cap);
    buf->cap = new_cap;
}

//往buf中追加数据,如果超出最大容量,自动扩容
void buf_append(buf_t *buf, char *data, int size){
    if  (buf->size + size > buf->cap) {
        buf_scale_up(buf, buf->size + size);
    }

    memcpy(buf->data + buf->size, data, size);
    buf->size += size;
}

//重置buf
void buf_reset(buf_t *buf){
    static int reset_times = 0;
    if ((buf->size << 1) < buf->cap) {
        reset_times++;
    }
    if (reset_times > 3) {
        buf_scale_down(buf, buf->cap);
        reset_times = 0;
    }
    memset(buf->data, 0, buf->size);
    buf->size = 0;
}

//销毁buf, 释放内存
void buf_destory(buf_t *buf){
    if (buf->data){
                free(buf->data);
                buf->data = NULL;
        }
        if (buf){
                free(buf);
                buf = NULL;
        }
}

我们在使用的时候,只需要像下面这样做就行了:

int main(void){
        buf_t *buf = buf_create();
        buf_append(buf, "hello", 5);
        buf_append(buf, ", world", 7);
        printf("buf: %s\n", buf->data);
        buf_destory(buf);
        return 0;
}

这种做法比之我们即用即申请本身已经优化了很多,即不需要频繁的mallocfree,而是尽量复用同一块内存,这样可以有效减少内存碎片。但是对于结构体本身和data,我们仍然是分开申请了两次,这一块还可以用柔性数组进行优化:

typedef struct buf_t {
        int     cap;    //buf的容量
        int     size;   //字符串实际长度
        char    data[]; //柔性数组
}buf_t;

#define BUF_CAP_DEFAULT 1046576

//根据长度申请buf
buf_t *buf_new_size(int cap){
    buf_t *buf = calloc(1, sizeof(buf_t) + cap * sizeof(char));
    buf->size = 0;
    buf->cap = cap;

    return buf;
}

//默认创建出1M大小的buf
buf_t *buf_create(){
    return buf_new_size(BUF_CAP_DEFAULT);
}

//内存扩容
void buf_scale_up(buf_t *buf, int cap){
    int new_cap = cap << 1;
    buf = realloc(buf, sizeof(buf_t) + new_cap * sizeof(char) );
    buf->cap = new_cap;
}

//内存缩容
void buf_scale_down(buf_t *buf, int cap){
    int new_cap = cap >> 1;
    buf = realloc(buf, sizeof(buf_t) + new_cap * sizeof(char) );
    buf->cap = new_cap;
}

//往buf中追加数据,如果超出最大容量,自动扩容
void buf_append(buf_t *buf, char *data, int size){
    if  (buf->size + size > buf->cap) {
        buf_scale_up(buf, buf->size + size);
    }

    memcpy(buf->data + buf->size, data, size);
    buf->size += size;
}

//重置buf
void buf_reset(buf_t *buf){
    static int reset_times = 0;
    if ((buf->size << 1) < buf->cap) {
        reset_times++;
    }
    if (reset_times > 3) {
        buf_scale_down(buf, buf->cap);
        reset_times = 0;
    }
    memset(buf->data, 0, buf->size);
    buf->size = 0;
}

//销毁buf, 释放内存
void buf_destory(buf_t *buf){
        if (buf){
                free(buf);
                buf = NULL;
        }
}

可以看到,除了涉及到内存申请的初始化、扩缩容不太一样之外,其他的实现都是一模一样的。它的调用方式没有发生任何改变:

int main(void){
        buf_t *buf = buf_create();
        buf_append(buf, "hello", 5);
        buf_append(buf, ", world", 7);
        printf("buf: %s\n", buf->data);
        buf_destory(buf);
        return 0;
}

该程序的运行结果:

[root@ck94 0size]# ./buf
buf: hello, world
[root@ck94 0size]# ./buf0
buf: hello, world

本专栏知识点是通过<零声教育>的系统学习,进行梳理总结写下文章,对C/C++课程感兴趣的读者,可以点击链接,查看详细的服务:C/C++Linux服务器开发/高级架构师

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

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

相关文章

FasterTransformer 002: cuda调试env

VSCODE ENV cmake NVIDIA Nsight Systems 当我们装好了CUDA的时候&#xff0c;其实在图形界面下已经装好了一个叫“nsight”的编译器&#xff0c;我们可以直接用终端打开这个编译器&#xff0c;然后写好程序直接编译然后debug就可以了。WINDOWS NVIDIA Nsight Systems 入门及…

【ARM AMBA APB 入门 1 -- APB总线介绍】

文章目录 APB 总线历史1.1 APB 总线介绍1.1.1 APB 使用场景1.1.2 APB 信号列表1.1.3 APB 状态机 1.2 APB 传输时序1.2.1 APB 写传输时序1.2.1.1 Write transfer with no wait states1.2.1.2 Write transfer with wait states 1.2.2 APB 读时序1.2.2.1 Read transfer with no wa…

C语言二级指针复习

之前写过一篇二级指针的博文&#xff0c;C语言二级指针Demo - Win32 版本_bcbobo21cn的博客-CSDN博客 下面复习一下二级指针&#xff1b; 二级指针的概念是这样&#xff0c; int a 100; int *p1 &a; int **p2 &p1; 有一个整型变量a被赋值100&#xff1b;…

【Python开发】FastAPI 10:SQL 数据库操作

在 FastAPI 中使用 SQL 数据库可以使用多个 ORM 工具&#xff0c;例如 SQLAlchemy、Tortoise ORM 等&#xff0c;类似 Java 的 Mybatis 。这些 ORM 工具可以帮助我们方便地与关系型数据库进行交互&#xff0c;如 MySQL 、PostgreSQL等。本篇文章将介绍如何使用 SQLAlchemy 来完…

osgb倾斜摄影三维模型数据web端在线管理平台,一键查看、分享

「四维轻云」是基于浏览器打造的一款osgb倾斜摄影三维模型数据web端在线管理平台&#xff0c;为用户提供了项目管理、团队管理、空间测量、场景编辑、在线标绘等功能&#xff0c;实现了osgb倾斜摄影三维模型数据在线管理、浏览和分享。 此外&#xff0c;为了更好地满足用户需求…

数据库信息速递 AWS因迁移PostgreSQL DBaaS而遭遇长时间停机时间而备受诟病

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

分享:win10使用 python 让 NVIDA GeForce MX250 显卡发挥余热,安装CUDA,cuDNN和PyTorch

目录 1. 更新最新的显卡驱动2. 安装CUDA3. 安装cuDNN4. 安装pytorch 1. 更新最新的显卡驱动 打开NVIDA更新驱动的官网地址 根据下图的选择&#xff0c;记得Windows驱动程序类型要选标准&#xff0c;如图 点击搜索&#xff0c;下面就会列出一大堆的历史驱动&#xff0c;选择第…

Stable Diffusion你需要知道的算法原理;ChatGPT新增函数调用;Adobe Illustrator引入AI工具Firefly;

&#x1f989; AI新闻 &#x1f680; OpenAI更新ChatGPT&#xff0c;新增函数调用、4倍上下文长度、更低的嵌入成本 摘要&#xff1a;OpenAI在官网发布了ChatGPT的更新细节&#xff0c;包括新增函数调用、GPT-4和GPT-3.5-Turbo的升级、降低成本等。其中&#xff0c;新增函数调…

接口使用https 为什么是一大段返回,而http是一行一行返回?

背景&#xff1a; 最近在调试chatgpt对话网站&#xff0c;因为返回数据比较大&#xff0c;就用到到了流读取。开始调用http一点都没有问题&#xff0c;但是上生产切换https时候始终抱错&#xff1f;这个问题纠结2天&#xff0c;终于在你chatgpt加持解决了 https 为什么是一大段…

Unity3D:场景视图视图选项

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D工具链 3D工具集&#xff1a; NSDT简石数字孪生 “场景视图视图选项”工具栏 您可以使用“场景视图视图选项”工具栏“叠加”来选择用于查看场景以及启用/禁用照明和音频的各种选项。这些控件仅在开发期间影响场景视图&#xff0…

mybatis二级缓存

MyBatis 缓存可以极大的提升查询效率。 MyBatis系统中默认定义两级缓存&#xff08;一级缓存和二级缓存&#xff09;。一、两级缓存 1、一级缓存&#xff1a;&#xff08;本地缓存&#xff09;&#xff1a;sqlSession级别的缓存。一级缓存是一直开启的&#xff1b;sqlSession级…

Kafka学习---4、消费者(分区消费、分区平衡策略、offset、漏消费和重复消费)

1、消费者 1.1 Kafka消费方式 1、pull&#xff08;拉&#xff09;模式&#xff1a;consumer采用从broker中主动拉取数据。 2、push&#xff08;推&#xff09;模式&#xff1a;Kafka没有采用这种方式。因为broker决定消息发生速率&#xff0c;很难适应所有消费者的消费速率。…

Hive的概念

Hive 概述 Hive是一个基于Hadoop的数据仓库系统&#xff0c;它提供了类似与SQL的查询语言HiveQL,可以将结构化数据存储在Hadoop分布式文件系统中&#xff0c;并通过MapReduce进行过处理。 Hive的目标是使数据分析师和其他人员能够使用SQL语言来查询大规模的数据集&#xff0…

Triton教程 --- Triton架构

Triton教程 —Triton架构 文章目录 Triton教程 ---Triton架构并发模型执行模型和调度器无状态模型状态模型控制输入隐式状态管理状态初始化调度策略直接的Oldest 合奏模型 下图显示了 Triton 推理服务器的高级架构。 模型存储库是一个基于文件系统的模型存储库&#xff0c;Trit…

链表--part8--环形链表(leetcode 142)

文章目录 基本思路leetcode 142 环形链表 基本思路 此题为重点题目 此题实际上可以分为俩题&#xff1a; 1 判断是否存在环 2 如果存在返回环的入口 如果不存在返回null 那我就对于这俩种情况进行相关的解释。 判断是否有环 有点类似追及问题&#xff0c;我们可以定义一个快指…

全面理解Starrocks3.0

文章目录 什么是Starrocks适用场景系统架构产品特性 什么是Starrocks StarRocks 是新一代极速全场景 MPP (Massively Parallel Processing) 数据库。StarRocks 的愿景是能够让用户的数据分析变得更加简单和敏捷。用户无需经过复杂的预处理&#xff0c;就可以用 StarRocks 来支…

Python3数据分析与挖掘建模(16)特征降维与特征衍生

1. 特征降维&#xff08;PCA&#xff09; 回顾知识点&#xff1a; 特征降维是指将高维特征空间的数据映射到低维空间的过程&#xff0c;以减少特征的数量并保留数据的主要信息。下面是特征降维的一般步骤&#xff1a; &#xff08;1&#xff09;求特征协方差矩阵&#xff1a…

I2C协议

i2c硬件电路 i2c总线连接图&#xff1a; 注&#xff1a; i2c的SDA和SCL都需要上拉电阻&#xff0c;改变上拉电阻大小可调节I2C总线的上拉强度&#xff0c;上拉电阻用1k~100k不等&#xff0c;小了驱动能力就强&#xff0c;但电流就大了。不同内核版本上的驱动不一样&#xff…

C语言之指针详解(3)

目录 本章重点 1. 字符指针 2. 数组指针 3. 指针数组 4. 数组传参和指针传参 5. 函数指针 6. 函数指针数组 7. 指向函数指针数组的指针 8. 回调函数 9. 指针和数组面试题的解析、 4. 数组参数、指针参数 我们来看一维数组传参 #include<stdio.h> void test(in…

Rust语言从入门到入坑——(2)Rust在windows上搭建开发环境

文章目录 0 引入1、搭建 Visual Studio Code 开发环境1.1、安装 Rust 编译工具1.2 、VS Code安装 2、官网在线3、总结4、引用 0 引入 开始搭建一个适合在windows上运行的Rust环境。 Rust支持的程序语言很多&#xff1a;可详见官网介绍 1、搭建 Visual Studio Code 开发环境 …