• 梦开始的地方——C语言柔性数组



    柔性数组

    什么是柔性数组?

    C99中,结构体最后一个元素它允许是一个未知大小的数组,这就叫做柔性数组成员

    这个概念听起来可能有点不可以思议,但它的确存在。

    来看这么一段代码

    struct Test
    {
    	int num;
    	int arr[0];//柔性数组成员
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上面那个写法有的编译器可能会报错,可以改成以下写法。

    struct Test
    {
    	int num;
    	int arr[];//柔性数组成员
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这里的arr数组就是一个柔性数组,它没有指定数组的大小,arr[0]这样的写法也非常奇怪。但这种写法只限于结构体的最后一个成员。柔性数组指的是数组的大小是柔性可变的。

    柔性数组的使用

    来看一段代码

    #include 
    #include 
    typedef struct Test
    {
    	int num;
    	int arr[0];
    }Test;
    int main()
    {
    	printf("%d\n", sizeof(Test));
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    打印结果

    4
    
    • 1

    计算这个结构体大小,发现这个数组是不占用任何空间的。那么柔性数组到底如何使用呢?

    错误写法

    struct Test t;//这样创建时错误的
    
    • 1

    正确的创建方法

    Test* t = (Test*)malloc(sizeof(Test) + 10 * sizeof(int));
    
    • 1

    通过malloc函数给这个结构体和柔性数组开辟了空间,就可以使用这一块空间了

    #include 
    #include 
    typedef struct Test
    {
    	int num;
    	int arr[0];
    }Test;
    int main()
    {
    	Test* t = (Test*)malloc(sizeof(Test) + 10 * sizeof(int));
    	int i = 0;
    	for (i = 0; i < 10; i++)
    	{
    		t->arr[i] = 100;
    	}
    
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    此时的内存布局

    在这里插入图片描述

    我们发现通过malloc函数给结构体开辟空间,就可以给柔性数组开辟空间。而这一块空间既然是用malloc函数开辟的,那么它就可以通过realloc函数来调整大小。这不就体现出了柔性数组的作用了吗?

    那么使用柔性数组有哪些注意事项呢?

    1. 结构体中的柔性数组成员前面至少要定义一个成员变量

      柔性数组前至少要有一个成员变量,才是合法的。

      typedef struct Test
      {
          int num;//前面至少要包含一个成员变量
      	int arr[];
      }Test;
      
      • 1
      • 2
      • 3
      • 4
      • 5
    2. **sizeof 返回的这种结构大小不包括柔性数组的内存 **

      这个前面已经演示过了,sizeof函数计算的结构体大小是不包含柔性数组大小的

    3. 包含柔性数组成员的结构用malloc函数进行内存的动态分配,分配的内存要大于结构体的大小

      比如下面这个代码,分配了结构体大小在加上40个字节的连续空间,那么柔性数组就能存储10个整形元素。

      sizeof(Test)开辟的空间是给num成员变量使用的,而后面的空间则是在给柔性数组使用的。

      #include 
      #include 
      typedef struct Test
      {
          int num;
      	int arr[];
      }Test;
      int main()
      {
      	Test* t = (Test*)malloc(sizeof(Test) + 10 * sizeof(int));
      	return 0;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

    柔性数组的优点

    提到动态扩容,不是指针也能做到吗?为什么还要有柔性数组这个玩样呢?

    柔性数组动态扩容和指针动态扩容对比

    #include 
    #include 
    typedef struct S1
    {
    	int num;
    	int* pArr;//指针
    }S1;
    typedef struct S2
    {
    	int num;
    	int arr[0];//柔性数组成员
    }S2;
    int main()
    {
    	//指针开辟
    	S1* ps1 = (S1*)malloc(sizeof(S1));
    	ps1->pArr = (int*)malloc(sizeof(int)*10);
    	//柔性数组开辟
    	S2* ps2 = (S2*)malloc(sizeof(S2) + sizeof(int) * 10);
    
    	//指针扩容
    	int* ptr1 = (int*)realloc(ps1->pArr,sizeof(int) * 20);
    	if (ptr1 != NULL)
    	{
    		ps1->pArr = ptr1;
    		ptr1 = NULL;
    	}
    	//柔性数组扩容
    	S2* ptr2 = (S2*)realloc(ps2,sizeof(S2) + sizeof(int) * 20);
    	if (ptr2 != NULL)
    	{
    		ps2 = ptr2;
    		ptr2 = NULL;
    	}
    	//指针释放
    	free(ps1->pArr);
    	free(ps1);
    	//柔性数组释放
    	free(ps2);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    指针开辟和柔性数组都能达到同样的效果,那么他们有什么不一样呢?

    在这里插入图片描述

    1. 方便内存释放,防止内存泄露

      柔性数组只需要一次free就能释放。而使用指针的方式就需要释放两次,假设通过指针的方式的代码是放的一个函数中给别人调用,就可能出现忘记释放指针指向的内存,从而导致内存泄露。

       //需要先释放掉指针的空间
       free(ps1->pArr);
       //再释放结构体
       free(ps1);
    
    • 1
    • 2
    • 3
    • 4
    1. 提高内存访问效率,减少内存碎片

      通过柔性数组开辟空间的方式一定是连续的,而通过指针开辟的方式则不一定是连续的。而内存访问连续的空间的效率会更高一点,如果不断的在内存中开辟空间,开辟的内存空间又不连续就会造成大量的内存碎片。导致空间被浪费。

      比如下面的,假设红色的是内存块,那么两个内存块之间的空间比较小,有些人程序想用但太小了,导致无法使用,内存碎片太多就导致了更多内存资源的浪费。但柔性数组开辟的内存空间一定是连续的,所以它可以减少内存碎片的产生。

    在这里插入图片描述


  • 相关阅读:
    Python【多分支选择结构】
    【无标题】
    软件测试面试遇到之redis要怎么测试?
    find_element_by_id()方法的使用上。这个方法已经被弃用,建议使用find_element()方法替代。
    高效、优雅的对象copy之MapStruct入门到精通,实战踩坑版
    useCallback,useMemo与react,memo的区别与联系
    第七章 排序
    MySql的安装配置超详细教程与简单的建库建表方法
    <排序及模拟实现>——《Data Structure in C Train》
    (五)Ansible-playbook
  • 原文地址:https://blog.csdn.net/weixin_53946852/article/details/128096130