缘由

最近在看redis的源码,刚开始看sds,看到这块代码

struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};

好奇为什么使用 char buf[],而不用char *buf,找到答案记录一下。

struct中的char*和char数组有什么区别

常用来构成缓冲区。比起指针,用空数组有这样的优势:

  • 不需要初始化,数组名就是所在的偏移,当数组名作为函数参数的时候,也就是指向第一个元素的指针。
  • 空数组不占用内存, 但是指针需要占用int长度的空间,不同平台,占用大小不一样。

内存连续性

  • 分配内存时:如果使用空数组,则可以使用 malloc(sizeof(structXXX) + buf_len)的方式来分配一段连续的内存。buf_len可以直接作为 缓冲区的长度。但是如果使用的是指针,则需要额外的去给指针赋值。而此时的内存与结构体的内存已经不连续,要分别申请和释放。
  • 在释放内存时,如果用数组,则一次性释放;如果用的是指针,则需要先释放struct中的指针,再释放结构体,不可以颠倒。

结构体中最后一个成员为[1]长度数组的用法:与长度为[0]数组的用法相同,改写为[1]是出于可移植性的考虑。有些编译器不支持[0]数组,可将其改成[][1].一般使用char [].

代码示例:

#include <stdlib.h>
#include <stdio.h>

struct __attribute__ ((__packed__)) t_char1
 {
    char a;
    char b[];
 };

struct __attribute__ ((__packed__)) t_char2
 {
    char    a;
    char*  b;
 };

int main()
{
    char a = 0;
    char a1[]={};
    char *b = &a;
    t_char1 c;
    t_char2 d;
    printf("char: %d, char*: %d, char[]: %d\n", sizeof(a), sizeof(b), sizeof(a1));
    printf("t_char1: %d, c.a: %p, c.b: %p\n",  sizeof(c), &c.a, c.b);
    printf("t_char2: %d, d.a: %p, d.b: %p\n",  sizeof(d), &d.a, d.b);
}

__attribute__ ((__packed__))的作用是为了取消内存对齐。

编译输出

$ g++ -o test main.cc -std=c++11
$ ./test

char: 1, char*: 8, char[]: 0
t_char1: 1, c.a: 0x7ffd906865b6, c.b: 0x7ffd906865b7
t_char2: 9, d.a: 0x7ffd906865a0, d.b: 0xa000000000004006

总结:

  • struct t_char1中空数组没有分配内存,sizeof©-sizeof(a) = 1-1=0; struct t_char2中指针的内存占用了八字节 。
  • 通过观察得到,在struct t_char1中, 空数组虽然不占用内存,但是可以获取结构体尾部的地址。
  • 在结构体中更推荐使用char[]

--完--