零长度数组没有意义?那是你不懂!看Linux内核中怎么高级玩它?(那是.不懂.数组.内核.长度...........)

wufei123 2025-01-05 阅读:9 评论:0
零长度数组的独特之处 对于许多人来说,零长度数组的概念可能有些奇怪,因为它似乎没有为存储数据而分配内存空间。然而,在linux内核中,我们经常可以看到零长度数组的身影。 零长度数组的定义 首先,我们需要明确零长度数组的含义。简而言之,零长...

零长度数组的独特之处

对于许多人来说,零长度数组的概念可能有些奇怪,因为它似乎没有为存储数据而分配内存空间。然而,在linux内核中,我们经常可以看到零长度数组的身影。

零长度数组没有意义?那是你不懂!看Linux内核中怎么高级玩它?

零长度数组的定义

首先,我们需要明确零长度数组的含义。简而言之,零长度数组就是一个长度为0的数组,也就是说,它不包含任何元素。零长度数组最早在C99标准中引入,并在C11中得到了进一步的支持。它的定义非常简单,就是一个大小为0的数组。例如:

int zero_length_array[0];

零长度数组的存在可能让人感到困惑,因为通常我们认为数组需要有至少一个元素来存储数据。然而,在某些特定的情况下,零长度数组却具有独特的用途和优势。

零长度数组的应用场景

零长度数组在Linux内核中被广泛应用,主要用于动态数组的实现。在这种情况下,零长度数组充当了柔性数组(flexible array)的角色。柔性数组是指数组的最后一个元素可以是一个未知大小的数组,这样可以根据需要动态地分配内存空间。

例如,在Linux内核的数据结构中,零长度数组常常用于表示可变长度的数据结构,如链表或者变长的网络数据包。通过零长度数组,开发者可以实现高效灵活的数据结构,避免了静态数组大小不足或过大的问题。

因此,尽管零长度数组在传统的C语言中看起来有些不合常规,但在特定的场景下,它却展现出了强大的应用价值和灵活性。

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.
");  
        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 length; i++) {  
        printk(KERN_INFO "%d ", array->data[i]);  
    }  
    printk(KERN_INFO "
");  
}  
  
// 内核模块初始化函数  
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的数组。但在编程中,它常常被用作一个占位符,或者作为一个结构体的最后一个元素,这样可以在结构体中灵活地存储更多的数据。

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

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

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

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

以上就是零长度数组没有意义?那是你不懂!看Linux内核中怎么高级玩它?的详细内容,更多请关注知识资源分享宝库其它相关文章!

版权声明

本站内容来源于互联网搬运,
仅限用于小范围内传播学习,请在下载后24小时内删除,
如果有侵权内容、不妥之处,请第一时间联系我们删除。敬请谅解!
E-mail:dpw1001@163.com

分享:

扫一扫在手机阅读、分享本文

发表评论
热门文章
  • 华为 Mate 70 性能重回第一梯队 iPhone 16 最后一块遮羞布被掀

    华为 Mate 70 性能重回第一梯队 iPhone 16 最后一块遮羞布被掀
    华为 mate 70 或将首发麒麟新款处理器,并将此前有博主爆料其性能跑分将突破110万,这意味着 mate 70 性能将重新夺回第一梯队。也因此,苹果 iphone 16 唯一能有一战之力的性能,也要被 mate 70 拉近不少了。 据悉,华为 Mate 70 性能会大幅提升,并且销量相比 Mate 60 预计增长40% - 50%,且备货充足。如果 iPhone 16 发售日期与 Mate 70 重合,销量很可能被瞬间抢购。 不过,iPhone 16 还有一个阵地暂时难...
  • 酷凛 ID-COOLING 推出霜界 240/360 一体水冷散热器,239/279 元

    酷凛 ID-COOLING 推出霜界 240/360 一体水冷散热器,239/279 元
    本站 5 月 16 日消息,酷凛 id-cooling 近日推出霜界 240/360 一体式水冷散热器,采用黑色无光低调设计,分别定价 239/279 元。 本站整理霜界 240/360 散热器规格如下: 酷凛宣称这两款水冷散热器搭载“自研新 V7 水泵”,采用三相六极马达和改进的铜底方案,缩短了水流路径,相较上代水泵进一步提升解热能力。 霜界 240/360 散热器的水泵为定速 2800 RPM 设计,噪声 28db (A)。 两款一体式水冷散热器采用 27mm 厚冷排,...
  • 惠普新款战 99 笔记本 5 月 20 日开售:酷睿 Ultra / 锐龙 8040,4999 元起

    惠普新款战 99 笔记本 5 月 20 日开售:酷睿 Ultra / 锐龙 8040,4999 元起
    本站 5 月 14 日消息,继上线官网后,新款惠普战 99 商用笔记本现已上架,搭载酷睿 ultra / 锐龙 8040处理器,最高可选英伟达rtx 3000 ada 独立显卡,售价 4999 元起。 战 99 锐龙版 R7-8845HS / 16GB / 1TB:4999 元 R7-8845HS / 32GB / 1TB:5299 元 R7-8845HS / RTX 4050 / 32GB / 1TB:7299 元 R7 Pro-8845HS / RTX 2000 Ada...
  • python中def什么意思

    python中def什么意思
    python 中,def 关键字用于定义函数,这些函数是代码块,执行特定任务。函数语法为 def (参数列表)。函数可以通过其名字和圆括号调用。函数可以接受参数作为输入,并在函数体中使用参数名访问。函数可以使用 return 语句返回一个值,它将成为函数调用的结果。 Python 中 def 关键字 在 Python 中,def 关键字用于定义函数。函数是代码块,旨在执行特定任务。 语法 def 函数定义的语法如下: def (参数列表): # 函数体 示例 定义...
  • python中int函数的用法

    python中int函数的用法
    int() 函数将值转换为整数,支持多种类型(字符串、字节、浮点数),默认进制为 10。可以指定进制数范围在 2-36。int() 返回 int 类型的转换结果,丢弃小数点。例如,将字符串 "42" 转换为整数为 42,将浮点数 3.14 转换为整数为 3。 Python 中的 int() 函数 int() 函数用于将各种类型的值转换为整数。它接受任何可以解释为整数的值作为输入,包括字符串、字节、浮点数和十六进制表示。 用法 int(object, base=10) 其中...