为什么我们需要柔性数组?
#define MAXLEN 1024
struct kd_node
{
struct kd_node*left;
struct kd_node*right;
int dim;
unsigned long long data[MAXLEN];
};
在这段代码中,为了存储数据,申请了长度为1024的unsigned long long型数组。若是数组长度远远小于MAXLEN。这样设计,便会是极其浪费空间的。如果MAXLEN太小,我们需要的数组长度又很大,这样也不合理。所以为了满足我们的需求,便出现了柔性数组,也称变长数组,可根据具体需求选择大小,故而拥有为变长或柔性,这一诱人的特点~~~
柔性数组的庐山真面目
struct sd_node
{
int num;
int size;
char data[];
};
柔性数组是大小待定的数组
在C语言中,可以使用结构体产生柔性数组, 在结构体的设计中,最后一项为数组,且其长度为0(或者不给出长度),这个数组就是柔性数组
注:全局数组或者局部数组不能这样定义
柔性数组的细节
在strcut sd_node结构体中data,仅仅是一个标识符,不占用存储空间。所以
sizeof(strcut sd_node)= 8
用途:长度为0的数组主要是满足长度可变的结构体
用法:在结构体的最后,声明一个长度为0的数组,就可以使得这个结构体是可变长的。对于编译器来说,此时长度为0的数组并不占用空间,因为数组名本身不占用空间,它只是一个偏移量,数组名这个符号本身代表了一个不可修改的地址常量。对于这个数组的大小,我们可以动态分配
int main()
{
const char str[] = "Hello Love_Hoeny_You";
struct sd_node*sp = (struct sd_node*)malloc(sizeof(struct sd_node) + 100);
sp->num = 5;
sp->size = 21;
strcpy(sp->data, str);
free(sp);
system("pause");
return 0;
}
图解:
优点:比起在结构体中声明一个指针变量,在进行动态分配的方法,这种方法效率更高
指针数据包:
#include <stdlib.h>
#include <stdio.h>
struct data_buff
{
int num; //块号
int size; //数据长度
char*data;
};
int main()
{
const char str[] = "Hello Love_Honey_You";
struct data_buff*ptr_buff = (struct data_buff*)malloc(sizeof(struct data_buff));
if (ptr_buff == NULL)
{
exit(1);
}
ptr_buff->size = strlen(str)+1;
ptr_buff->data = (char*)malloc(sizeof(char)*ptr_buff->size);
if (ptr_buff->data == NULL)
{
exit(1);
}
strcpy(ptr_buff->data, str);
printf("OVER\n");
free(ptr_buff->data);
free(ptr_buff);
ptr_buff = NULL;
system("pause");
return 0;
}
注:释放时要向释放ptr_buff->data,再释放ptr_buff。如果顺序相反,则会丢掉ptr_buff->data内存储的地址,造成内存泄漏。并且释放了ptr_buff,再去释放ptr_buff->data,编译器会报错~~~
变长数据缓冲区:
#include <stdio.h>
#include <stdlib.h>
struct data_buff
{
int num; //块号
int len; //数据长度
char data[];
};
int main()
{
const char str[] = "Love_Honey_You~~~";
struct data_buff*p_buff = (struct data_buff*)malloc(sizeof(struct data_buff) + sizeof(char)*(strlen(str) + 1));
if (p_buff == NULL)
{
exit(1);
}
strcpy(p_buff->data, str);
free(p_buff);
system("pause");
return 0;
}
缺点:在结构体中,长度为0的数组必须在最后声明,在设计结构体类型有一定限制
注:如果结构体是通过 calloc,malloc或者realloc等动态分配方式生成,在不需要时要释放相应的空间
sizeof 计算带有柔性数组的结构体大小
#include <stdio.h>
#include <stdlib.h>
struct data_buff
{
int num;
int len;
char data[]; //data为标识符不占用空间
};
int main()
{
struct data_buff test = { 1, strlen(), "My_Love_Girl" }; //栈上的柔性数组
int len = sizeof(struct data_buff);
system("pause");
return 0;
}
注:sizeof计算的是数据类型所占的字节数,并且是在编译时就确定了
动态创建二维数组
#include <stdio.h>
#include <stdlib.h>
int main()
{
int cos = 0; //行
int row = 0; //列
scanf("%d%d", &cos, &row);
int**s = (int **)malloc(sizeof(int*)*cos);
for (int i = 0; i < cos; i++)
{
s[i] = (int *)malloc(sizeof(int)*row);
}
for (int i = 0; i < cos; i++)
{
for (int j = 0; j < row; j++)
{
s[i][j] = i + j;
printf("%d ", s[i][j]);
}
printf("\n");
}
for (int i = 0; i < cos; i++)
{
free(s[i]);
}
free(s);
system("pause");
return 0;
}
s里存放的是指针数组中第一个元素的地址,指针数组中第一个元素存放的是第一行整型数组中第一个元素的地址,指针类型不同+1能力不同,通过下标运算符可方便访问二维数组的每一个元素
举个例子,在栈上创建数组 int arr[3] [4]
arr[2][3]与s[2][3]的区别
(1) &arr表示整个二维数组的地址,而&s表示的是存放指针数组中第一个元素地址的空间的地址
(2) arr表示第一行的地址,arr+2-----》表示偏移到第三行,*(arr+2)表示第三行第一个元素的地址
*(arr+2)+3---》表示偏移到第三行第四个元素的地址---》*(*(arr+2)+3)---》表示第三行第四个元素本身
(3) s表示指针数组中第一个元素的地址,s+2---》表示偏移到指针数组中第三个元素的地址
*(s+2)---》表示第三行第一个元素的地址,*(s+2)+3---》表示第三行第四个元素的地址
*(*(s+2)+3)表示第三行第四个元素的本身
(4) arr+1要跳过16个字节,而s+1只跳过4个字节
注:动态的二维数组和栈上的二维数组的区别是,动态的二维数组在内存中不一定是连续的,它是通过指针串起来的,所以他只是在逻辑上是连续的。而栈上的二维数组不管是逻辑还是内存中都是连续的