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

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

文章目录

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

1. 定长数组

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

#include 
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)的概念,允许我们在创建数组的时候使用变量

int n = a + b;
int arr[n];

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

变长数组特点:

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

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

#include 
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;
}

C语言定长数组 变长数组 柔性数组_第1张图片

3. 柔性数组

那么有没有一种数组,在确定确定数组的大小,还可以根据需要扩大数组
在C99 中,结构体中的最后⼀个元素允许是未知⼤⼩的数组,这就叫做『柔性数组』成员。
例如:

struct S
{
	int i;
	char c;
	int arr[];
};

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

变长数组的特点:

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

这样的数组就是柔性数组

3.1 结构体的大小

struct S
{
	int i;
	char c;
	int arr[];
};

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

代码运行结果:>8
根据结构体对齐规则,int 4个字节,char 1个字节,总共5个字节,不为最大对齐数4的倍数,对齐至8字节

在存在柔性数组的结构体中,计算结构体的大小时,不会计算柔性数组的大小

3.2 柔性数组的使用

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

代码一:

#include 
#include 
#include 

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这篇博客

代码二:

#include 
#include 
#include 

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. ⽅便内存释放
    代码一:
    示意图为连续内存,只是为了区分划分为了几块,红色部分为柔性数组调整大小的部分
    C语言定长数组 变长数组 柔性数组_第2张图片
    代码二:
    示意图为连续内存,只是为了区分划分为了几块,红色部分为柔性数组调整大小的部分
    C语言定长数组 变长数组 柔性数组_第3张图片
    代码一的空间是一次性在堆区上开辟的,而代码二是先开辟结构体的空间,再开辟柔性数组的空间的,而既然是开辟的空间,就需要使用free来释放,否则有可能导致内存泄漏,而代码一只需要一次释放,而代码二中结构体往往知道释放,但是结构体成员也需要释放容易被忽视,所以,如果我们把结构体的内存以及其成员要的内存⼀次性分配好了,并返回给⼀个结构体指针,做⼀次free就可以把所有的内存也给释放掉

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

你可能感兴趣的:(初识C语言,c语言,柔性数组,定长数组,变长数组,malloc,realloc,free)