C语言定长数组 变长数组 柔性数组
文章目录
- C语言定长数组 变长数组 柔性数组
- 1. 定长数组
- 2. 变长数组
- 3. 柔性数组
- 3.1 结构体的大小
- 3.2 柔性数组的使用
1. 定长数组
在C99标准之前,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)的概念,允许我们在创建数组的时候使用变量
int n = a + b;
int arr[n];
在上述示例中,arr 就是变长数组,因为它的长度取决于变量n的值,编译器没法事先确定,只有运行时才能知道变量n是多少
变长数组特点:
- 变长数组⻓度只有运⾏时才能确定,所以变⻓数组不能初始化
- 变⻓数组的意思是数组的⼤⼩是可以使⽤变量来指定的,在程序运⾏的时候,根据变量的⼤⼩来指定数组的元素个数,⽽不是说数组的⼤⼩是可变的。数组的⼤⼩⼀旦确定就不能再变化了。
在VS2022中是不支持变长数组的,没法测试,在gcc编译器上可以测试
#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 中,结构体中的最后⼀个元素允许是未知⼤⼩的数组,这就叫做『柔性数组』成员。
例如:
struct S
{
int i;
char c;
int arr[];
};
在上述示例中,arr就是变长数组
变长数组的特点:
- 在结构体中
- 最后一个成员
- 未知大小的数组
这样的数组就是柔性数组
3.1 结构体的大小
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的方式来实现数组大小的扩大的
代码一:
#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这篇博客
代码二:
#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;
}
代码一和代码二可以使用了柔性数组,但是代码一有两个好处:
- ⽅便内存释放
代码一:
示意图为连续内存,只是为了区分划分为了几块,红色部分为柔性数组调整大小的部分
代码二:
示意图为连续内存,只是为了区分划分为了几块,红色部分为柔性数组调整大小的部分
代码一的空间是一次性在堆区上开辟的,而代码二是先开辟结构体的空间,再开辟柔性数组的空间的,而既然是开辟的空间,就需要使用free来释放,否则有可能导致内存泄漏,而代码一只需要一次释放,而代码二中结构体往往知道释放,但是结构体成员也需要释放容易被忽视,所以,如果我们把结构体的内存以及其成员要的内存⼀次性分配好了,并返回给⼀个结构体指针,做⼀次free就可以把所有的内存也给释放掉
2.这样有利于访问速度
代码一中是一块连续的内存,连续的内存有益于提⾼访问速度,也有益于减少内存碎⽚。(只是相较于代码二速度会快一点,其实,我个⼈觉得也没多⾼了,反正你跑不了要⽤做偏移量的加法来寻址)