C语言定长数组 变长数组 柔性数组

C语言定长数组 变长数组 柔性数组

文章目录

  • [C语言定长数组 变长数组 柔性数组](#C语言定长数组 变长数组 柔性数组)
    • [1. 定长数组](#1. 定长数组)
    • [2. 变长数组](#2. 变长数组)
    • [3. 柔性数组](#3. 柔性数组)
      • [3.1 结构体的大小](#3.1 结构体的大小)
      • [3.2 柔性数组的使用](#3.2 柔性数组的使用)

1. 定长数组

在C99标准之前,C语言在创建数组的时候,数组的大小只能使用常量常量表达式来,或者在初始化数组时,省略数组的大小,这就是所谓的定长数组

c 复制代码
#include <stdio.h>
int main()
{
	int arr1[10];
	int arr2[10 + 5];
	int arr3[] = {1,2,3,4,5,6,7,8,9,10};
	return 0;
}

2. 变长数组

有定长数组这样的语法限制,让我们创建数组不够灵活,有时数组大小给大了浪费空间,有时数组大小给小了不够用,所以在C99标准之后,有个一个**变⻓数组(variable-length array,简称 VLA)**的概念,允许我们在创建数组的时候使用变量

c 复制代码
int n = a + b;
int arr[n];

在上述示例中,arr 就是变长数组 ,因为它的长度取决于变量n的值,编译器没法事先确定,只有运行时才能知道变量n是多少

变长数组特点:

  1. 变长数组⻓度只有运⾏时才能确定,所以变⻓数组不能初始化
  2. 变⻓数组的意思是数组的⼤⼩是可以使⽤变量来指定的,在程序运⾏的时候,根据变量的⼤⼩来指定数组的元素个数,⽽不是说数组的⼤⼩是可变的。数组的⼤⼩⼀旦确定就不能再变化了。

在VS2022中是不支持变长数组的,没法测试,在gcc编译器上可以测试

c 复制代码
#include <stdio.h>
int main()
{
 int n = 0;
 scanf("%d", &n);//根据输⼊数值确定数组的⼤⼩
 int arr[n];
 int i = 0;
 for (i = 0; i < n; i++)
 {
 scanf("%d", &arr[i]);
 }
 for (i = 0; i < n; i++)
 {
 printf("%d ", arr[i]);
 }
 return 0;
}

3. 柔性数组

那么有没有一种数组,在确定确定数组的大小,还可以根据需要扩大数组

在C99 中,结构体中的最后⼀个元素允许是未知⼤⼩的数组,这就叫做『柔性数组』成员。

例如:

c 复制代码
struct S
{
	int i;
	char c;
	int arr[];
};

在上述示例中,arr就是变长数组

变长数组的特点:

  1. 在结构体中
  2. 最后一个成员
  3. 未知大小的数组

这样的数组就是柔性数组

3.1 结构体的大小

c 复制代码
struct S
{
	int i;
	char c;
	int arr[];
};

#include <stdio.h>
int main()
{
	printf("%zd\n", sizeof(struct S));
	return 0;
}

代码运行结果:>8

根据结构体对齐规则,int 4个字节,char 1个字节,总共5个字节,不为最大对齐数4的倍数,对齐至8字节
在存在柔性数组的结构体中,计算结构体的大小时,不会计算柔性数组的大小

3.2 柔性数组的使用

柔性数组是通过 malloc realloc的方式来实现数组大小的扩大的

代码一:

c 复制代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct S
{
	int i;
	char c;
	int arr[];
};
int main()
{
	struct S* p = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int)); //通过结构体指针来访问结构体,开辟一个结构体大小 + 想要开辟数组大小的空间
	if (p == NULL)  //判断开辟是否成功
	{
		perror("malloc fail");
		return 1;
	}
	//使用柔性数组
	int i = 0;  
	for (i = 0; i < 10; i++) 
	{
		p->arr[i] = i;
	}
	struct S* tmp = (struct S*)realloc(p, sizeof(struct S) + 15 * sizeof(int)); //调整数组大小
	if (tmp != NULL)  //判断是否调整成功
	{
		p = tmp;
	}
	else
	{
		perror("realoc fail");
		return 1;
	}
	//使用
	for (i = 10; i < 15; i++)
	{
		p->arr[i] = i;
	}
	//打印
	for (i = 0; i < 15; i++)
	{
		printf("%d ", p->arr[i]);
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}

看不懂的可以去看看动态内存管理malloc calloc realloc free这篇博客

代码二:

c 复制代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct S
{
	int i;
	char c;
	int *arr;
};
int main()
{
	struct S* p = (struct S*)malloc(sizeof(struct S));  //开辟一块空间给结构体
	if (p == NULL)  //判断开辟是否成功
	{
		perror("malloc fail");
		return 1;
	}
	p->arr = (int*)malloc(10 * sizeof(int));  //开辟数组
	if (p->arr == NULL)  //判断开辟是否成功
	{
		perror("malloc fail");
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		p->arr[i] = i;
	}
	int* tmp = (int*)realloc(p->arr,15 * sizeof(int)); //调整结构体大小
	if (tmp != NULL) //判断
	{
		p->arr = tmp;
	}
	else
	{
		perror("realoc fail");
		return 1;
	}
	//使用
	for (i = 10; i < 15; i++)
	{
		p->arr[i] = i;
	}
	//打印
	for (i = 0; i < 15; i++)
	{
		printf("%d ", p->arr[i]);
	}
	//释放
	free(p->arr);
	p->arr = NULL;
	free(p);
	p = NULL;
	return 0;
}

代码一代码二可以使用了柔性数组,但是代码一有两个好处:

  1. ⽅便内存释放
    代码一:
    示意图为连续内存,只是为了区分划分为了几块,红色部分为柔性数组调整大小的部分

    代码二:
    示意图为连续内存,只是为了区分划分为了几块,红色部分为柔性数组调整大小的部分

    代码一的空间是一次性在堆区上开辟的,而代码二是先开辟结构体的空间,再开辟柔性数组的空间的,而既然是开辟的空间,就需要使用free来释放,否则有可能导致内存泄漏,而代码一只需要一次释放,而代码二中结构体往往知道释放,但是结构体成员也需要释放容易被忽视,所以,如果我们把结构体的内存以及其成员要的内存⼀次性分配好了,并返回给⼀个结构体指针,做⼀次free就可以把所有的内存也给释放掉

2.这样有利于访问速度

代码一中是一块连续的内存,连续的内存有益于提⾼访问速度,也有益于减少内存碎⽚。(只是相较于代码二速度会快一点,其实,我个⼈觉得也没多⾼了,反正你跑不了要⽤做偏移量的加法来寻址)

相关推荐
爱吃西瓜的小菜鸡12 分钟前
【C语言】判断回文
c语言·学习·算法
FeboReigns2 小时前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++
FeboReigns3 小时前
C++简明教程(文章要求学过一点C语言)(2)
c语言·开发语言·c++
_小柏_3 小时前
C/C++基础知识复习(43)
c语言·开发语言·c++
yoyobravery3 小时前
c语言大一期末复习
c语言·开发语言·算法
落羽的落羽7 小时前
【落羽的落羽 C语言篇】自定义类型——结构体
c语言
Kisorge8 小时前
【C语言】代码BUG排查方式
c语言·开发语言·bug
yoyo勰9 小时前
sqlite3
c语言·sqlite
就爱学编程10 小时前
重生之我在异世界学编程之C语言:数据在内存中的存储篇(上)
c语言·数据结构
意疏10 小时前
【C 语言指针篇】指针的灵动舞步与内存的神秘疆域:于 C 编程世界中领略指针艺术的奇幻华章
c语言·开发语言·指针