gcc 0长数组学习
首先,我们要知道,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长数组学习的更多相关文章
- C/C++ 中的0长数组(柔性数组)
转自C/C++ 中的0长数组(柔性数组) 在标准C和C++中0长数组如charArray[0]是不允许使用的,因为这从语义逻辑上看,是完全没有意义的.但是,GUN中却允许使用,而且,很多时候,应用在了 ...
- GCC 中零长数组与变长数组
前两天看程序,发现在某个函数中有下面这段程序: int n; //define a variable n int array[n]; //define an array with length n 在 ...
- C语言变长数组data[0]总结
C语言变长数组data[0] 1.前言 今天在看代码中遇到一个结构中包含char data[0],第一次见到时感觉很奇怪,数组的长度怎么可以为零呢?于是上网搜索一下这样的用法的目的,发现在linux内 ...
- C语言变长数组 struct中char data[0]的用法
版权声明:本文为博主原创文章,未经博主允许不得转载. 今天在看一段代码时出现了用结构体实现变长数组的写法,一开始因为忘记了这种技术,所以老觉得作者的源码有误,最后经过我深思之后,终于想起以前看过的用s ...
- 【C语言高级编程】你见过长度为0的数组吗?管你信不信,看就完了!
一.什么是零长度数组 零长度数组就是长度为0的数组. ANSI C 标准规定:定义一个数组时,数组的长度必须是一个常数,即数组的长度在编译的时候是确定的.在ANSI C 中定义一个数组的方法如下: 类 ...
- PL/SQL 嵌套表变长数组和索引表[转]
关于PL/SQL中这三种数组的介绍,不想写了.转一篇日志吧…… 链接:http://www.blogjava.net/decode360/archive/2008/08/08/280825.html ...
- 【数据库】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 ( ...
- js数组学习整理
原文地址:js数组学习整理 常用的js数组操作方法及原理 1.声明数组的方式 var colors = new Array();//空的数组 var colors = new Array(3); // ...
- C语言格式化输入不定长数组
先随便写写,有空再整理. 直接贴代码 #include <stdio.h> #include <stdlib.h> //从一行标准输入中格式化输入一个不定长数组 void in ...
随机推荐
- JAVA各种系统架构图及其简介
1.spring架构图 Spring是一个开源框架,是为了解决企业应用程序开发复杂性而创建的.框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为J2EE应用程序开发提供集成的框 ...
- 使用Axure RP原型设计实践06,登录验证
登录验证主要功能包括: ● 用户名错误,提示无效用户名,用户名和密码文本框清空● 用户名存在,密码错误,提示密码错误,密码清空,焦点进入密码框● 用户名和密码都正确,验证通过 本篇接着"使用 ...
- 如何修改Mac截屏保存路径
MAC OS X系统默认的截图路径是桌面文件夹,默认的截图格式是 PNG 图片格式,如何自定义设置呢? 截图保存路径 打开终端(Terminal)并输入如下命令: defaults write com ...
- Java获取当前类名的两种方法
适用于非静态方法:this.getClass().getName() 适用于静态方法:Thread.currentThread().getStackTrace()[1].getClassName() ...
- Mysql一个非常有用的内置函数今天碰到要把MySQL数据库中的varchar转换成date类型进
Mysql一个非常有用的内置函数 今天碰到要把MySQL数据库中的varchar转换成date类型进行时间的比较和查询.在网上找了找,发现MySQL也跟其他数据库一样有自己内置的转换函数:str_to ...
- ios成长之每日一遍(day 1)
Hello world开始. 这里不讨论如何创建项目导入项目.由于趁上班时间打酱油所以也不谈细节, 只谈具体项目的实现与关键流程的解析, 只供本人实际程况使用.不喜请移驾. 首先来谈谈 AppDele ...
- 数学图形(1.47)贝塞尔(Bézier)曲线
贝塞尔曲线又称贝兹曲线或贝济埃曲线,是由法国数学家Pierre Bézier所发现,由此为计算机矢量图形学奠定了基础.它的主要意义在于无论是直线或曲线都能在数学上予以描述. 上一节讲的是高次方程曲线, ...
- go语言之进阶篇并行和并发的区别与go语言并发优势
1.并行和并发的概念 并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行. 并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在 ...
- cesium原理篇(二)--网格划分【转】
转自:http://www.cnblogs.com/fuckgiser/p/5772077.html 上一篇我们从宏观上介绍了Cesium的渲染过程,本章延续上一章的内容,详细介绍一下Cesium网格 ...
- easyui +ASP.NET 前后台乱码解决方法
前端接口 decodeURIencodeURI 后台接口 System.Web.HttpUtility.UrlEncode(user.name) 凡是涉及到中文的地方都要使用这几个接口