0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

嵌入式中零长度数组基本操作方法

嵌入式开发星球 来源:嵌入式开发爱好者 作者:嵌入式开发爱好者 2024-05-11 08:49 次阅读

C语言零长度数组,听起来可能有点奇怪,因为它没有分配内存空间,无法存储数据。但实际上,零长度数组在Linux内核中随处可见。

wKgaomY_FmCASuqKAACr-GPuQ2c182.png

零长度数组的定义

首先,我们要明白什么是零长度数组。简单来说,零长度数组就是一个长度为0的数组,也就是说不包含任何元素的数组。零长度数组在C99标准中引入,并在C11中得到进一步的支持。其定义很简单,就是一个大小为0的数组。例如:

int a[0];

在Linux内核中,零长度数组通常不会直接这样使用,而是作为结构体中最后一个元素,配合动态内存分配来使用。

零长度数组在Linux内核中的应用案例

在Linux内核中,经常可以看到零长度数组被用作结构体末尾的占位符,以表示结构体的可变长度部分。例如,一个表示网络套接字的struct sockaddr结构体可能如下所示:

struct sockaddr { sa_family_t sa_family; // 地址家族,如AF_INET, AF_UNIX等 char sa_data[14]; // 对于IPv4,这里实际上只有12字节被使用 };

在这个例子中,sa_data字段实际上是一个填充字段,用于容纳不同地址家族的地址数据。由于地址家族可能不同,所需的数据长度也可能不同,因此这里使用了一个足够大的固定长度数组。然而,如果使用零长度数组,代码会更加清晰:

struct sockaddr { sa_family_t sa_family; // 地址家族 char sa_data[0]; // 可变长度部分,实际使用时会动态分配 };

在实际应用中,内核代码会结合动态内存分配来设置需要的的sa_data长度,并填充相关的数据。零长度数组可以与kmalloc、vmalloc等内存分配函数结合使用,来实现这种动态分配,所以有人也把零长度数组称为柔性数组。

如何具体实现结构体动态内存分配?

在Linux内核或其他C语言编写的底层系统中,零长度数组经常被用作灵活的数据结构的一部分,特别是在需要动态增长或缩小的数组中。以下是一个简单的示例,展示了如何在内核编程中使用零长度数组来实现一个可变长度的整数数组:

#include // 包含printk等内核函数 #include // 包含kmalloc和kfree等内存管理函数 // 定义一个结构体,用于表示可变长度的整数数组 struct variable_int_array { size_t length; // 数组当前长度 int data[0]; // 零长度数组,实际数据存储在这里 }; // 创建一个新的可变长度整数数组 struct variable_int_array *create_int_array(size_t initial_length) { // 分配内存,包括结构体本身和初始长度的整数数组 struct variable_int_array *array = kmalloc( sizeof(struct variable_int_array) + initial_length * sizeof(int), GFP_KERNEL ); if (!array) { // 内存分配失败 return NULL; } // 初始化数组长度 array->length = initial_length; // 返回新创建的数组 return array; } // 销毁一个可变长度整数数组 void destroy_int_array(struct variable_int_array *array) { if (!array) { // 空指针检查 return; } // 释放内存 kfree(array); } // 向数组中添加一个新的整数 void add_int_to_array(struct variable_int_array **array_ptr, int value) { struct variable_int_array *array = *array_ptr; size_t new_length = array->length + 1; // 分配新的内存块,包含扩展后的数组 array = kmalloc( sizeof(struct variable_int_array) + new_length * sizeof(int), GFP_KERNEL ); if (!array) { // 内存分配失败 printk(KERN_ERR "Failed to extend the integer array.n"); return; } // 复制旧数组的值到新数组 memcpy(array->data, (*array_ptr)->data, array->length * sizeof(int)); // 添加新值 array->data[new_length - 1] = value; // 更新数组长度 array->length = new_length; // 释放旧数组 kfree(*array_ptr); // 更新指向数组的指针 *array_ptr = array; } // 打印数组内容 void print_int_array(struct variable_int_array *array) { for (size_t i = 0; i < array- >length; i++) { printk(KERN_INFO "%d ", array->data[i]); } printk(KERN_INFO "n"); } // 内核模块初始化函数 static int __init my_module_init(void) { struct variable_int_array *my_array = create_int_array(2); if (!my_array) { // 处理错误 return -ENOMEM; } // 添加一些值 add_int_to_array(&my_array, 10); add_int_to_array(&my_array, 20); // 打印数组 print_int_array(my_array); // 销毁数组 destroy_int_array(my_array); return 0; } // 内核模块退出函数 static void __exit my_module_exit(void) { // 清理工作(如果有的话) } // 注册模块初始化和退出函数 module_init(my_module_init); module_exit(my_module_exit); // 定义模块许可证 MODULE_LICENSE("GPL");

在这个例子中,忽略内核模块相关部分,重点看结构体variable_int_array相关几个函数。

我们定义了一个名为variable_int_array的结构体,它包含一个length字段和一个零长度数组data。使用create_int_array函数来分配内存并初始化这个结构体,同时使用destroy_int_array函数来释放内存。add_int_to_array函数允许我们向数组中添加新的整数,它会动态地重新分配内存以容纳新增加的元素。最后,print_int_array函数用来打印输出出结构体中整数动态数组成员值。

下面具体来看看重点代码的实现。

create_int_array函数创建一个新的可变长度整数数组的结构体variable_int_array,函数形参initial_length是要创建数组初始长度。第13行使用kmalloc动态分配结构体初始内存空间,这里包括结构体本身和初始长度为initial_length的整数数组空间。第24行就是把initial_length,也即是初始数据长度值存到结构体length成员中,因为长度不是0了而是initial_length。

destroy_int_array就是调用kfree释放上面创建的内存空间,这个比较简单。

重点看看add_int_to_array(struct variable_int_array **array_ptr, int value)函数,这个函数就是将一个新的整数值动态添加到数组中,这也是最麻烦的过程。

第一个形参是结构体array_ptr,是个二级指针,指向旧的结构体内存首地址,注意这个指针变量后面新分配内存空间地址要存入其中。第二个形参value是被添加的新的整数值。

第43行是将旧的结构体首地址存到array指针中。

第44行new_length暂时保存数组长度。

第47行是分配新的内存空间,并将首地址存入array变量,注意从此以后array指向新空间。因为数组新加了一个整数,所以空间变大,要重新分配,新分配的空间大小包括之前旧的结构体长度和新添加的一个整数的空间大小。

第59行是将旧的数组数据拷贝到新的数组空间中。

第62行就是新的整数值添加到新数组空间最后一个位置,到此数组空间数据更新完成。

第66行更新结构体的length成员为new_length,其实就是加了个1。

第69行,释放之前旧结构体的所有内存,因为长度增加分配了新内存了。

第72行就是将新空间地址赋给array_ptr指针变量,这是让指向旧结构体首地址的指针指向新的结构体首地址了,到此就结束了。

总结

简单来说,零长度数组就是一个长度为0的数组。但在编程中,它常常被用作一个占位符,或者作为一个结构体的最后一个元素,这样可以在结构体中灵活地存储更多的数据。

那么,零长度数组有什么价值和意义呢?

灵活性:零长度数组允许我们在不知道具体需要多少存储空间的情况下,先分配一个基本的结构体。这样,我们可以在后续的程序执行中,根据需要动态地添加数据到这个零长度数组中。这种灵活性对于处理可变大小的数据非常有用。

内存效率:通过动态地分配内存给零长度数组,我们可以避免一开始就分配过多的内存,这样可以更加高效地利用内存资源。只有当我们确实需要额外的存储空间时,才会分配额外的内存。

简化代码:在某些情况下,使用零长度数组可以简化代码结构。比如,我们可以将一些相关的数据都放在一个结构体中,而零长度数组可以作为这个结构体的最后一个元素,用于存储额外的数据。这样,我们可以更方便地管理和操作这些数据。

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    5057

    文章

    18964

    浏览量

    301822
  • 内核
    +关注

    关注

    3

    文章

    1359

    浏览量

    40183
  • Linux
    +关注

    关注

    87

    文章

    11199

    浏览量

    208691
  • 内存
    +关注

    关注

    8

    文章

    2962

    浏览量

    73803
  • C语言
    +关注

    关注

    180

    文章

    7591

    浏览量

    135800
收藏 人收藏

    评论

    相关推荐

    嵌入式系统串口通信帧的同步方法

    制下面介绍一下简化的串口通信数据帧结构,以便分析说明嵌入式系统串口通信过程的帧同步方法。 假定串口发送的数据帧结构为: 其中:包头用于同步,一般是一个或多个ASCII字符,本文中假定数据帧同步头有2
    发表于 10-09 19:17

    怎么才能在使用ss集线器时丢失零长度数据包?

    上的每次读之前都有一个写在端点0x01上的操作,它的长度是预先知道的。读取端点0x82没有可预测长度,因此FPGA每20毫秒生成一个零长度,以完成当前事务并唤醒主机。下面是我在主机侧启
    发表于 10-18 10:45

    systemd定时器的基本操作方法

    定时器任务作为嵌入式系统中常见的应用,systemd 定时器为用户提供更多的可配置功能以及优化选项。本文列举了 systemd 定时器基本操作方法,以及和cron 对比,帮助用户更快得使用。更多的技术细节和功能请参考下面的链接内容。
    发表于 01-01 07:37

    如何配置嵌入式服务器

    嵌入式Web服务器每个Spring Boot Web应用程序都包含一个嵌入式Web服务器。此功能会导致许多操作方法问题,包括如何更改嵌入式服务器以及如何配置
    发表于 10-27 08:35

    嵌入式操作系统Linux 的串口应用编程

    针对嵌入式Linux操作系统的特点,分析在该系统下串行通信口编程控制的方法,总结程序设计的步骤; 在嵌入式Linux 系统上, 编写控制程序, 成功地实现
    发表于 05-14 14:34 28次下载

    汽车电子嵌入式操作系统

    汽车电子嵌入式操作系统
    发表于 10-30 16:07 9次下载
    汽车电子<b class='flag-5'>中</b><b class='flag-5'>嵌入式</b><b class='flag-5'>操作</b>系统

    关于 Java 数组的 12 个最佳方法

    下文加介绍的是stackoverflow关于数组方法的相关问题中,获得最多票数的12个数组操作方法
    发表于 01-29 09:45 888次阅读

    Java数组的基本操作方法整理

    本文主要介绍了Java数组的基本操作方法整理,是Java入门学习的基础知识。数组是具有相同数据类型的一组数据的集合,Java支持多为数组
    发表于 01-29 10:15 1198次阅读

    基于μC/OS嵌入式操作系统的嵌入式数据管理设计

    较高要求,只能应用在比较高端的嵌入式系统。在低端的嵌入式系统,传统的数据管理方法是对数据存储空间按顺序编号,数据存储与删除均根据编号顺序
    发表于 10-09 16:24 1214次阅读
    基于μC/OS<b class='flag-5'>嵌入式</b><b class='flag-5'>操作</b>系统的<b class='flag-5'>嵌入式</b>数据管理设计

    嵌入式操作系统

    2.2 实时操作系统的评价指标三、基于Linux的嵌入式操作系统3.1 ARMLinux简介3.2 uCLinux简介四、嵌入式操作系统设计
    发表于 11-03 18:36 46次下载
    <b class='flag-5'>嵌入式</b><b class='flag-5'>操作</b>系统

    C语言开发可能会用到的GNU

        为了方便使用,GNU C在标准C语言的基础上进行了部分方便开发的扩展。 这里讲解一些开发可能会用到的,或者使用频率比较高的内容。 零长度数组和变量长度数组   GNU C 允许使用
    的头像 发表于 11-17 10:41 1599次阅读

    0长度数组不占用存储空间

    由于0长度数组是GNU C的扩展,有一些巧妙编写的诡异代码,其执行结果就是依赖于编译器和优化策略的实现的,我们来看看以下代码。
    的头像 发表于 09-28 15:18 734次阅读
    0<b class='flag-5'>长度数组</b>不占用存储空间

    什么是嵌入式操作系统?

    嵌入式操作系统通常在嵌入式系统工作。嵌入式系统是支持机器的计算机。它在更大的机器上执行一项任务。示例包括汽车
    的头像 发表于 12-23 15:33 7601次阅读
    什么是<b class='flag-5'>嵌入式</b><b class='flag-5'>操作</b>系统?

    零长数组如何使用定长包定义数据缓冲区

    零长数组 请先思考以下问题: C语言中,数组长度是否可以为0? 如果要接收一个不定长数据包,你会如何定义数据缓冲区? 第一个问题 : 在标准C语言中,没有长度为0的
    的头像 发表于 09-27 14:58 627次阅读

    嵌入式工控机如何使用?嵌入式工控机操作方法及注意事项

    嵌入式工控机作为现代工业自动化和控制系统重要的硬件支持,广泛应用于生产线监控、轨道交通、电力能源等多个领域。其稳定性、可靠性以及适应恶劣环境的能力,使其成为许多行业的首选。那么,嵌入式工控机究竟如何使用呢?高能计算机将详细介绍
    的头像 发表于 10-18 10:02 180次阅读