【柔性数组】0长度数组,可变数组

简介: 【柔性数组】0长度数组,可变数组

前言

在c99标准中,引入了“柔性数组”的概念

即在c99中,结构体的最后一个成员可以是一个未知大小的数组(没有长度的数组)

这个数组就叫做**【柔性数组】成员**;

柔性数组可以根据使用者的需求进行malloc,确保数组在使用中能保证足够且不过多浪费空间。

例:

struct SC
{
  int a;
  char c;
  int arr[];
  /*int arr[0]; 若是编译器报错也可写成该形式*/
};
int main()
{
  struct SC s;
  printf("%d ", sizeof(s));
  return 0;
}

柔性数组的特点

柔性数组在计算大小时是不参与到结构体大小之中的,在结构体中,柔性数组属于一个未知大小的数组,即编译器也不能确定的大小。

柔性数组的使用

#include<stdio.h>
//声明存在柔性数组的结构体
struct SC
{
  int a;
  char c;
  int arr[];
  /*int arr[0]; 若是编译器报错也可写成该形式*/
};
int main()
{
  //使用malloc函数为带柔性数组的结构体变量进行动态内存分配
  struct SC* s = (struct SC*)malloc(sizeof(struct SC) + 10 * sizeof(int));
  if (s == NULL) {//判断返回指针是否有效
    perror("malloc");
    return 1;
  }
  else {
  //使用
    s->a = 10;
    s->c = 'c';
    for (int i = 0; i < 10; i++) {
      s->arr[i] = i+1;
    }
    for (int i = 0; i < 10; i++) {
      printf("%d ", s->arr[i]);
    }
  }
  //释放内存 - 指针置空
  free(s);
  s = NULL;
  return 0;
}

声明一个存在柔性数组成员的结构体,并使用该类型创建一个指针变量,利用该指针malloc所需要的内存即可

同时,在使用柔性数组应注意

  • 结构中的柔性数组成员前面必须至少存在一个其他成员
    在使用柔性数组时应确保该结构体除了柔性数组成员还应拥有其他成员,根据上文可知,柔性数组为一个未知大小的数组,故若是结构体中只存在柔性数组,则不能确定结构体大小 - error
  • 柔性数组成员应存在于结构体内所有成员的最后一位
  • 若是使用sizeof( )计算结构体大小返回值不包括柔性数组成员
  • 柔性数组的大小使用malloc函数进行初始化
    包含柔性数组成员的结构体使用malloc函数进行动态内存分配,且内存分配的大小必须大于结构体的大小,以适应柔性数组的预期大小

不使用柔性数组实现可变数组

若是不使用柔性数组,能使用其他方式来实现类似于柔性数组的可变效果吗?

#include<errno.h>
#include<string.h>
#include<stdlib.h>
struct SC
{
  int a;
  char c;
  int* p;
};
int main()
{
  //第一次malloc
  struct SC* py = (struct SC*)malloc(sizeof(struct SC));
  if (py == NULL){//判断malloc返回的指针是否有效
    printf("%s\n", strerror(errno));
    return 1;
  }
  //第二次malloc
  py->p = (int*)malloc(10 * sizeof(int));
  if (py->p == NULL) {
    printf("%s\n", strerror(errno));
    return 1;
  }
  //使用
  py->a = 10;
  py->c = 'w';
  for (int i = 0; i < 10; i++) {
    py->p[i] = i + 1;
  }
  printf("%d %c\n", py->a, py->c);
  for (int i = 0; i < 10; i++) {
    printf("%d ", py->p[i]);
  }
  //释放
  //释放时应按照顺序进行释放
  free(py->p);
  py->p = NULL;
  free(py);
  py = NULL;
  return 0;
}

在结构体中不设置柔性数组,取而代之的是一个整形的指针,可根据需要来变换指针的类型;

  • 第一次malloc:
    malloc结构体变量的大小存放至结构体指针中,确保可通过结构体指针访问到相应的空间。
  • 第二次malloc:
    第二次malloc则是通过结构体内部的指针来开辟一个根据需求所定的大小用于存放数组数据模拟柔性数组。通过指针来链接各个内存空间。

两者的区别

若是将柔性数组定为方案一,不使用柔性数组实现可变数组定为方案二;

哪种方案的效率会更高?

  • 方案一优先:
柔性数组 非柔性数组
malloc 一次 两次
free 一次 两次
置空 一次 两次
  • malloc函数为动态内存分配,但是malloc进行多次动态内存分配时所分配到的空间并不是连续的,所以在每块动态内存空间中,将会存在内存碎片;因为内存碎片的大小十分小,故他们的被利用率将大大降低。
  • 且从上文可知每次进行动态开辟内存时再用完或者不用时应该及时释放,若不释放程序结束内存也会由操作系统进行回收,但若是程序未结束,也未释放内存,则会导致内存泄漏的问题;
    多次malloc意味着需要更多次的free释放以及多次的置空指针,也意味着代码的维护难度要加大。

注意事项

在使用柔性数组时应注意:

  • 使用柔性数组时,柔性数组成员前至少需要存在一位其他成员;
  • 在使用sizeof计算结构体大小时,柔性数组不参与其中;
  • 带有柔性数组成员的结构体应使用malloc函数进行动态内存分配,且分配的动态内存必须大于结构体的大小,以适应柔性数组的预期大小。


相关文章
|
Shell 数据安全/隐私保护
执行jobs命令查看不到任务的原因终于找到了
背景 执行nohup command &后,断开终端,执行jobs命令查看不到任务的原因,终于找到了。 首先执行完如下步骤: 1.nohup scp user@server:path/file localpath 2.输入密码 3.按Ctrl+Z挂起当前进程 4.使用命令bg让挂起的进程继续运行
|
12月前
|
安全 开发工具 数据安全/隐私保护
代码管理记录(一): 码云Gitee代码提交和维护
本文介绍了Gitee平台,提供了代码托管服务,并详细说明了从新建仓库到代码提交的步骤。
334 1
代码管理记录(一): 码云Gitee代码提交和维护
|
12月前
|
计算机视觉
Opencv错误笔记(一):通过cv2保存图片采用中文命名出现乱码
在使用OpenCV的cv2模块保存带有中文命名的图片时,直接使用cv2.imwrite()会导致乱码问题,可以通过改用cv2.imencode()方法来解决。
588 0
Opencv错误笔记(一):通过cv2保存图片采用中文命名出现乱码
|
7月前
|
NoSQL Ubuntu 网络安全
在 Ubuntu 20.04 上安装和配置 Redis
在 Ubuntu 20.04 上安装和配置 Redis 的步骤如下:首先更新系统包,然后通过 `apt` 安装 Redis。安装后,启用并启动 Redis 服务,检查其运行状态。可选配置包括修改绑定 IP、端口等,并确保防火墙设置允许外部访问。最后,使用 `redis-cli` 测试 Redis 功能,如设置和获取键值对。
276 1
|
12月前
|
存储 大数据 Python
案例学Python:filter()函数的用法,高级!
`filter()`函数是Python中处理序列数据的强大工具,它允许我们高效地根据条件过滤元素。通过结合匿名函数、常规函数或直接利用Python的内置逻辑,`filter()`提供了灵活且高效的过滤机制,尤其在大数据处理和内存敏感的应用中展现出其价值。掌握 `filter()`的使用,不仅能提升代码的可读性和效率,还能更好地适应Python的函数式编程风格。
353 2
|
Ubuntu Linux 数据安全/隐私保护
内核实验(七):使用内核KFIFO环形缓冲区机制
本文通过一个内核模块实验,演示了如何在Linux内核中使用KFIFO环形缓冲区机制,包括定义KFIFO、编写驱动程序以及在Qemu虚拟机中进行编译、部署和测试,展示了KFIFO在无需额外加锁的情况下如何安全地在读者和写者线程间进行数据传输。
655 0
内核实验(七):使用内核KFIFO环形缓冲区机制
|
移动开发 网络协议 网络安全
详解 WebSocket
详解 WebSocket
462 0
|
监控 安全 关系型数据库
稳定性之故障应急处理流程
尽管可以通过稳定性体系建设,来避免出现生产系统故障。但是仍然无法彻底避免一点风险都不会产生,当稳定性风险产生后,怎么快速协调组织,缩短故障时长,科学的流程呢?
稳定性之故障应急处理流程
|
安全 算法 编译器
【C++ 基础 ()和{}括号】深入探索 C++ 的变量初始化:括号和大括号的奥秘
【C++ 基础 ()和{}括号】深入探索 C++ 的变量初始化:括号和大括号的奥秘
962 0
|
存储 安全 Ubuntu
【Linux 应用开发 】Linux环境下动态链接库路径(RPATH)的调整策略
【Linux 应用开发 】Linux环境下动态链接库路径(RPATH)的调整策略
1282 1