首先,我们要知道,0长度的数组在ISO C和C++的规格说明书中是不允许的。这也就是为什么在VC++2012下编译你会得到一个警告:“warning C4200: 使用了非标准扩展 : 结构/联合中的零大小数组”。

那么为什么gcc可以通过而连一个警告都没有?那是因为gcc 为了预先支持C99的这种玩法,所以,让“零长度数组”这种玩法合法了。关于GCC对于这个事的文档在这里:“Arrays of Length Zero”,文档中给了一个例子(我改了一下,改成可以运行的了):

#include <stdlib.h>
#include <string.h>
 
struct line {
   int length;
   char contents[]; // C99的玩法是:char contents[]; 没有指定数组长度
};
 
int main(){
    int this_length=;
    struct line *thisline = (struct line *)
                     malloc (sizeof (struct line) + this_length);
    thisline->length = this_length;
    memset(thisline->contents, 'a', this_length);
    return ;
}

上面这段代码的意思是:我想分配一个不定长的数组,于是我有一个结构体,其中有两个成员,一个是length,代表数组的长度,一个是contents,代码数组的内容。后面代码里的 this_length(长度是10)代表是我想分配的数据的长度。(这看上去是不是像一个C++的类?)这种玩法英文叫:Flexible Array,中文翻译叫:柔性数组。

我们来用gdb看一下:

(gdb) p thisline
$ = (struct line *) 0x601010
 
(gdb) p *thisline
$ = {length = , contents = 0x601010 "\n"}
 
(gdb) p thisline->contents
$ = 0x601014 "aaaaaaaaaa"

我们可以看到:在输出*thisline时,我们发现其中的成员变量contents的地址居然和thisline是一样的(偏移量为0×0??!!)。但是当我们输出thisline->contents的时候,你又发现contents的地址是被offset了0×4了的,内容也变成了10个‘a’。(我觉得这是一个GDB的bug,VC++的调试器就能很好的显示)

我们继续,如果你sizeof(char[0])或是 sizeof(int[0]) 之类的零长度数组,你会发现sizeof返回了0,这就是说,零长度的数组是存在于结构体内的,但是不占结构体的size。你可以简单的理解为一个没有内容的占位标识,直到我们给结构体分配了内存,这个占位标识才变成了一个有长度的数组。

看到这里,你会说,为什么要这样搞啊,把contents声明成一个指针,然后为它再分配一下内存不行么?就像下面一样。

struct line {
   int length;
   char *contents;
};
 
int main(){
    int this_length=;
    struct line *thisline = (struct line *)malloc (sizeof (struct line));
    thisline->contents = (char*) malloc( sizeof(char) * this_length );
    thisline->length = this_length;
    memset(thisline->contents, 'a', this_length);
    return ;
}

这不一样清楚吗?而且也没什么怪异难懂的东西。是的,这也是普遍的编程方式,代码是很清晰,也让人很容易理解。即然这样,那为什么要搞一个零长度的数组?有毛意义?!

这个事情出来的原因是——我们想给一个结构体内的数据分配一个连续的内存!这样做的意义有两个好处:

第一个意义是,方便内存释放。如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。(读到这里,你一定会觉得C++的封闭中的析构函数会让这事容易和干净很多)

第二个原因是,这样有利于访问速度。连续的内存有益于提高访问速度,也有益于减少内存碎片。(其实,我个人觉得也没多高了,反正你跑不了要用做偏移量的加法来寻址)

我们来看看是怎么个连续的,用gdb的x命令来查看:(我们知道,用struct line {}中的那个char contents[]不占用结构体的内存,所以,struct line就只有一个int成员,4个字节,而我们还要为contents[]分配10个字节长度,所以,一共是14个字节)

(gdb) x /14b thisline
0x601010:                                                   
0x601018:                                    

从上面的内存布局我们可以看到,前4个字节是 int length,后10个字节就是char contents[]。

如果用指针的话,会变成这个样子:

(gdb) x /16b thisline
0x601010:                                                       
0x601018:                                                    
(gdb) x /10b this->contents
0x601020:                                                
0x601028:            

上面一共输出了四行内存,其中,

  • 第一行前四个字节是 int length,第一行的后四个字节是对齐。
  • 第二行是char* contents,64位系统指针8个长度,他的值是0×20 0×10 0×60 也就是0×601020。
  • 第三行和第四行是char* contents指向的内容。

从这里,我们看到,其中的差别——数组的原地就是内容,而指针的那里保存的是内容的地址

注:该文转自酷客,选取了其中的关于0长数组的部分,以前0长数组也见过,但是为什么要用呢,有什么好处呢,通过该文应该有一个了解!

gcc 0长数组学习的更多相关文章

  1. C/C++ 中的0长数组(柔性数组)

    转自C/C++ 中的0长数组(柔性数组) 在标准C和C++中0长数组如charArray[0]是不允许使用的,因为这从语义逻辑上看,是完全没有意义的.但是,GUN中却允许使用,而且,很多时候,应用在了 ...

  2. GCC 中零长数组与变长数组

    前两天看程序,发现在某个函数中有下面这段程序: int n; //define a variable n int array[n]; //define an array with length n 在 ...

  3. C语言变长数组data[0]总结

    C语言变长数组data[0] 1.前言 今天在看代码中遇到一个结构中包含char data[0],第一次见到时感觉很奇怪,数组的长度怎么可以为零呢?于是上网搜索一下这样的用法的目的,发现在linux内 ...

  4. C语言变长数组 struct中char data[0]的用法

    版权声明:本文为博主原创文章,未经博主允许不得转载. 今天在看一段代码时出现了用结构体实现变长数组的写法,一开始因为忘记了这种技术,所以老觉得作者的源码有误,最后经过我深思之后,终于想起以前看过的用s ...

  5. 【C语言高级编程】你见过长度为0的数组吗?管你信不信,看就完了!

    一.什么是零长度数组 零长度数组就是长度为0的数组. ANSI C 标准规定:定义一个数组时,数组的长度必须是一个常数,即数组的长度在编译的时候是确定的.在ANSI C 中定义一个数组的方法如下: 类 ...

  6. PL/SQL 嵌套表变长数组和索引表[转]

    关于PL/SQL中这三种数组的介绍,不想写了.转一篇日志吧…… 链接:http://www.blogjava.net/decode360/archive/2008/08/08/280825.html ...

  7. 【数据库】4.0 MySQL入门学习(四)——linux系统环境下MySQL安装

    1.0 我的操作系统是CentOS Linux release 7.6.1810  (Core) 系统详细信息如下: Linux version 3.10.0-957.1.3.el7.x86_64 ( ...

  8. js数组学习整理

    原文地址:js数组学习整理 常用的js数组操作方法及原理 1.声明数组的方式 var colors = new Array();//空的数组 var colors = new Array(3); // ...

  9. C语言格式化输入不定长数组

    先随便写写,有空再整理. 直接贴代码 #include <stdio.h> #include <stdlib.h> //从一行标准输入中格式化输入一个不定长数组 void in ...

随机推荐

  1. EBS已安装模块

    /* Formatted on 2018/3/15 11:14:51 (QP5 v5.256.13226.35538) */ SELECT fa.application_short_name , fp ...

  2. 【linux】linux命令grep + awk 详解

    linux命令grep  +  awk 详解 grep:https://www.cnblogs.com/flyor/p/6411140.html awk:https://www.cnblogs.com ...

  3. 【Elasticsearch】ES中时间查询报错:Caused by: ElasticsearchParseException[failed to parse date field [Sun Dec 31 16:00:00 UTC 2017] with format [yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis]];

    ES中时间查询报错:Caused by: ElasticsearchParseException[failed to parse date field [Sun Dec 31 16:00:00 UTC ...

  4. h.264 x.264

    这是两个不同类型的东西. x264是视频编码器,H.264是视频编码格式. x264编出来的东西就是h.264的.举个例子来说,x264就好比画图或者photoshop,h.264就好比jpg,bmp ...

  5. byte[]数组的正则表达式搜索 z

    在byte[]数组的特定位置进行正则表达式匹配. 为了从硬盘上搜索特定类型的文件,需要根据文件的特征值进行匹配. 对于已掌握文件结构的文件,采用hard-code的方式进行匹配:这样速度快: 对于未掌 ...

  6. WordPress主题开发:get_term_by和get_term_link

    学习目的: 某一个分类的名称.别名.和id都可以到后台自己去找,但这样找比较麻烦还容易看错,wordpress提供了下面两个函数get_term_by和get_term_link,只要提供别名.名称或 ...

  7. Git:常用命令(一)

    取得项目的Git 仓库 从当前目录初始化 git init 初始化后,在当前目录下会出现一个名为.git 的目录,所有Git 需要的数据和资源都存放在这个目录中.不过目前,仅仅是按照既有的结构框架初始 ...

  8. mysql递归查询子类ID查询所有子类

    先来看数据表的结构如下: id  name    parent_id  ---------------------------  1   Home        0  2   About        ...

  9. Material Designer的低版本兼容实现(四)—— ToolBar

       Toolbar其实是一个ActionBar的变体,大大扩展了Actionbar.我们可以像对待一个独立控件一样去使用ToolBar,可以将它放到屏幕的任何位置,不必拘泥于顶部,还可以将它改变高度 ...

  10. 通过AnimationSet设置动画

    在代码中可以通过set来设置多个动画属性,这里分开来设置不同的属性. 首先先贴上布局文件,里面的imageview是用来做动画的控件 <RelativeLayout xmlns:android= ...