今天在看 redis 源码的时候了解到一个不熟悉的名词 柔性数组
本着遇到问题解决问题,遇到疑惑解决疑惑的态度动手试试看看是不是和我想的那样。
在看到 SDS 动态字符串的时候,刚开始看到这个结构,就直接认为这个 buf 想到了 一个 char 类型的指针。
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
然后就写 demo 测试.
#include "stdlib.h"
#include "stdio.h"
typedef unsigned short uint16_t;
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
int age = 12;
int buf[4] = {1, 2, 3, 4};
int main() {
struct sdshdr16 s;
s.len = 1;
s.alloc = 2;
s.flags = 0b11;
// 这里有问题
s.buf = (char *) malloc(sizeof(char) * 200);
return 0;
}
这里一直被卡在 s.buf = (char *) malloc(sizeof(char) * 200); 这里。
...
中途省略 n 多字
...
一直不知道原来,再回头看书,发现我忽视了最重要的一个关键词, 柔性数组。
终于了解了。
在 C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员。在使用柔性数组成员时,需要注意如下几点:
- 结构中的柔性数组成员前面至少包含一个其他成员。
- 柔性数组成员允许结构中包含一个大小可变的数组。
- sizeof 返回的这种结构大小不包括柔性数组的内存。
- 包含柔性数组成员的结构用 malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
也就是说在给这个结构体分配内存的时候,不管内存分配多大,除了前面需要用到的,剩下的空间都是这个数组的。
验证一下:
#include "stdlib.h"
#include "stdio.h"
typedef unsigned short uint16_t;
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
int main() {
// 这个时候 s1->buf 应该有200个长度
struct sdshdr16 *s1 = (struct sdshdr16 *) malloc(sizeof(struct sdshdr16) + 27);
// 32
// 32
struct sdshdr16 *s2 = (struct sdshdr16 *) malloc(sizeof(struct sdshdr16) + 27);
printf("%d", sizeof(struct sdshdr16));
s1->len = 0;
s1->alloc = 200;
s1->flags = 0b11;
for (int i = 0; i < 40; i++) {
s1->buf[i] = 'a' + i % 26;
}
printf("\ns1->buf[40]: %d", &s1->buf[27]);
printf("\ns2: %d", s2);
// s1->buf[40]
s1->buf[43] = 31;
printf("\ns2->len: %d", s2->len);
return 0;
}
这里验证了一下确实是这样的,目前给 s1 分配了 32 个字节,除了前面用到的 5 个字节,后面的 27 个字节都是给 buf 的
所以 buf[27] 的地址与 s2 的地址一样。因为 s1, s2 是一起分配的。所以在空间上是连续的。
这里需要注意的是,这里会在分配内存的时候会进行字节对齐,如果我这边给 buf 分配的不是 27 个字节,而是其他的,比如 40 个字节,加上结构体前面的 5 个字节,共 45 个字节,字节对齐的话,s2 应该从第 48 个字节起分配内存,也就是 buf[43]的地址为 s2 的地址。