今天在公司作代码review时,发现了一个我无法理解的代码,第一次看见感觉这尼玛是个大bug啊~~~ 类似的代码是这样的:

typedef struct line {
    int length;
    char contents[0];
}str_line;

一般在定义数组的时候,一般都要指定一个长度,让编译器提前知道这个数组的长度。申明一个长度为0的数组是个什么鬼,这肯定是一个严重级别的问题啊! 03.jpg

但仔细想想,这样的代码如果存在问题的话,前辈们不可能没发现,而且目前的版本也如此的稳定。

Google一下先,于是发现了这个

Zero-length arrays are allowed in GNU C. They are very useful as the last element of a structure that is really a header for a variable-length object

struct line {
  int length;
  char contents[0];
};

struct line *thisline = (struct line *)malloc (sizeof (struct line) + this_length);
thisline->length = this_length;

什么? 它不仅没错,而且非常的有用? 120.jpg

仔细看这个网页给出的例子,发现它的用法确实非常的妙啊,可以说让C语言有了一个可变长度的数组啊。

自己写了个例子来体验一下它的妙处

/**
 * Created by dongyayun on 16/1/20.
 */
#include<stdio.h>

typedef struct line {
    int length;
    char contents[0];
}str_line;

typedef struct circle {
    int diameter;
    int something;
}str_circle;


int main() {
    //譬如这个例子里, str_a 所占的内存是sizeof(str_line), 剩余的 sizeof(str_circle) 字节的内存的首地址就是contents;
    str_circle str_b = {1, 2};
    str_line *pStr_a = (str_line *) malloc(sizeof(str_line) + sizeof(str_circle));
    if(pStr_a == NULL) {
        return 1;
    }
    memset(pStr_a,0,sizeof(str_line) + sizeof(str_circle));

    printf("sizeof line = %lu\r\n",sizeof(str_line));

    printf("str_a 's address = %x \r\n",pStr_a);
    printf("contents 's address = %x \r\n",pStr_a->contents); //这里的地址偏移相对于pStr_a 偏移 sizeof(str_line) 长度

    //我们可以利用这块内存存放 circle;
    pStr_a->length = sizeof(str_circle);
    memcpy(pStr_a->contents,&str_b,sizeof(str_circle));

    printf("diameter = %u, something = %u \r\n", ((str_circle *)(pStr_a->contents))->diameter, ((str_circle *)(pStr_a->contents))->something);

    free(pStr_a); // 这里只需要释放一次就行了,因为他是连续的内存
    return 0;
}

执行结果:

dongyayundeMacBook-Pro:c_test dongyayun$ ./a.out
sizeof line = 4
str_a 's address = 73401ed0
contents 's address = 73401ed4
diameter = 1, something = 2

从执行结果来看,sizeof line 的长度是4个字节,所以长度为0的数组占用这个结构体的内存是0; 长度为0的数组所指向的内存的首地址刚好是结构体的首地址偏移结构体所占字节的大小的地址

  • 长度为0的数组并不占有内存空间,而指针方式需要占用内存空间。

  • 对于长度为0数组,在申请内存空间时,采用一次性分配的原则进行;对于包含指针的结构体,才申请空间时需分别进行,释放时也需分别释放。

  • 对于长度为的数组的访问可采用数组方式进行

C语言有这样的类型,那么Java有没有呢,答案是肯定的 00

看这样两段代码:

  • 代码一:
public String[] getStrings() {
    if( foo ) {
        return null;
    } else {
        return new String[] {"bar, "baz"};
    }
}

String[] strings = getStrings();
if (strings != null) {
     for (String s : strings) {
      blah(s);
     }
}
  • 代码二:
public String[] getStrings() {
    if( foo ) {
        return new String[0];
    } else {
        return new String[] {"bar, "baz"};
    }
}

// the if block is not necessary anymore
String[] strings = getStrings();
for (String s : strings) {
    blah(s);
}

返回一个长度为0的数组,就不需要判断所取到的数组是否为null了,好赞 ~~ 25