头图

前言

本来是解决frr在交叉编译时,编译mips架构不可用的问题,后来排查到是因为一个数组的问题,然后无意中看到一个词---零长数组。嘿,还挺新鲜,以前真没用过。研究了一下,感觉还挺实用。

例子

废话不多少,先上例子,看咋用:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_LENGTH 10

// 0长度数组
struct zero_buffer {
    int len;
    char data[0];
};

// 定长数组
struct max_buffer {
    int len;
    char data[MAX_LENGTH];
};

// 指针
struct point_buffer {
    int len;
    char *data;
};

// 函数,演示三种不同的数据结构
void demonstrateDataStructures() {
    // 共用的数据
    int data_length = 15;
    char data[] = "Hello, GPT!";
    
    // 使用零长数组
    struct zero_buffer *zbuffer = malloc(sizeof(struct zero_buffer) + sizeof(char) * data_length);
    
    if (zbuffer) {
        zbuffer->len = data_length;
        strcpy(zbuffer->data, data);

        printf("Zero-length Array: Length=%d, Data=%s\n", zbuffer->len, zbuffer->data);

        // 释放内存
        free(zbuffer);
    }

    // 使用定长数组
    struct max_buffer mbuffer;
    mbuffer.len = data_length;
    strcpy(mbuffer.data, data);

    printf("Fixed-length Array: Length=%d, Data=%s\n", mbuffer.len, mbuffer.data);

    // 使用指针
    struct point_buffer pbuffer;
    pbuffer.len = data_length;
    pbuffer.data = malloc(sizeof(char) * data_length);

    if (pbuffer.data) {
        strcpy(pbuffer.data, data);

        printf("Pointer Array: Length=%d, Data=%s\n", pbuffer.len, pbuffer.data);

        // 释放内存
        free(pbuffer.data);
    }
}

int main(void) {
    demonstrateDataStructures();

    return 0;
}

打印:

Zero-length Array: Length=15, Data=Hello, GPT!
Fixed-length Array: Length=15, Data=Hello, GPT!
Pointer Array: Length=15, Data=Hello, GPT!

零长数组

零长数组是GCC(GNU Compiler Collection)的一个特定扩展功能,不是标准C语言的一部分。它是一种GNU C扩展,也称为C语言的柔性数组(Flexible Array Member)。

这个扩展功能允许在结构体的末尾定义一个长度为零的数组,以实现动态内存分配和数据存储。它在某些情况下非常有用,特别是在需要构建变长数据结构时,如动态字符串或变长记录。

需要注意的是,零长数组的可移植性有限,因为它不是C标准的一部分,因此在使用零长数组时,代码可能会依赖于特定的编译器扩展。如果要确保代码在不同编译器上都能正常工作,建议谨慎使用零长数组,或者考虑使用其他方式来实现相同的功能,比如指针数组或动态内存分配。

详细解释:

  • 定义:一个结构体内的数组,其长度为零。它通常用在结构体的末尾,充当一个占位符,用于在运行时动态分配内存以存储可变长度的数据
  • 不占用额外的内存:零长数组本身不占用额外的内存空间,即它不增加结构体的大小。相当于一个指向变长数据的起始位置的引用,它的长度在运行时根据需要动态分配。
  • 动态内存分配: 为了使用零长数组来存储数据,你需要使用动态内存分配函数,如malloc,来分配足够的内存以容纳零长数组中的数据。这个分配的内存块的大小由结构体的大小和实际数据长度的总和决定。
  • 数据存储: 一旦内存分配完成,你可以将数据存储在零长数组中,就像使用普通数组一样。你可以使用各种C字符串和数据操作函数来处理零长数组中的数据。
  • 动态长度: 零长数组允许你在运行时更改数据的长度,因为它不会限制你的数据存储大小。这对于实现动态数据结构非常有用,例如可变大小的字符串。
  • 内存管理:与使用malloc分配的内存一样,你需要小心管理零长数组的内存。确保在不再需要数据时释放内存,以防止内存泄漏。

为什么会有零长数组

定长数组

  • 固定大小: 定长数组的大小是固定的,这意味着如果你需要存储不同长度的数据,你必须为最大可能的长度分配内存,可能会浪费内存。
  • 内存浪费: 如果你的数据长度远小于数组的最大大小,会导致内存浪费。例如,如果数组大小是100,但你只需要存储10个元素,就会浪费大量内存。

指针

  • 避免内存浪费:如果将定长数组char data[MAX_LENGTH];换成 char *data;,避免了内存浪费,相对来说只浪费了一个指针域的空间。
  • 内存泄漏:过程相对繁琐一些:

    • 首先,要为结构体申请一块内存;
    • 其次哈要为char *data申请空间;
    • 用完之后上面的空间都要释放,如果不谨慎,很大可能会造成内存泄漏。

那零长到底占用空间么

以下仅为个人理解。
零长数组本身并不存储数据,但它可以作为一个占位符,用于动态分配和管理内存,以存储数据。
例如上面的例子,首先使用malloc分配了足够的内存来容纳零长数组的数据,并使用strcpy将数据复制到零长数组中。
零长数组的作用在于允许动态地分配不同长度的数据,而不占用额外的内存空间。
零长数组的关键是它不会占用额外的内存空间,但一旦你将数据复制到零长数组中,它会占用zbuffer指针所指向的那块内存空间。零长数组本身不会增加这块内存的大小,但它可以灵活地存储不同长度的数据。

一旦使用strcpy将数据复制到zbuffer中,zbuffer所指向的内存块中的一部分就被占用了。这个占用的内存大小由你分配的内存块的大小决定。当你使用malloc分配内存时,分配的内存大小足够容纳sizeof(struct zero_buffer)和数据长度的总和。

 

永远不要停止追求卓越,因为你的梦想值得每一分努力。
欢迎关注个人博客沟通交流

NULL
30 声望0 粉丝