前两天看程序,发现在某个函数中有下面这段程序:

int n;              //define a variable n
int array[n]; //define an array with length n

在我所学的C语言知识中,这种数组的定义在编译时就应该有问题的,因为定义数组时,数组的长度必须要是一个大于0的整型字面值或定义为 const 的常量。例如下面这样

int array1[10];     //valid
int const N = 10;
int array2[N]; //valid
int n = 10;
int array3[n]; //invalid

但从上面看第三种定义数组的方法也是正确的,于是,我用 gcc 去编译这段程序,发现确实没报错,而且我对此数组进行一些操作,结果也都是正确!这简直颠覆了我的知识框架!难道大学老师教我的、我平时看的书,都是错误的吗?!我开始寻找答案...

C 语言中变长数组

最官方的解释应该是 C 语言的规范和编译器的规范说明了。

  • 在 ISO/IEC9899 标准的 6.7.5.2 Array declarators 中明确说明了数组的长度可以为变量的,称为变长数组(VLA,variable length array)。(注:这里的变长指的是数组的长度是在运行时才能决定,但一旦决定在数组的生命周期内就不会再变。)
  • 在 GCC 标准规范的 6.19 Arrays of Variable Length 中指出,作为编译器扩展,GCC 在 C90 模式和 C++ 编译器下遵守 ISO C99 关于变长数组的规范。

这下,终于安心了,原来这种语法确实是 C 语言规范,GCC 非常完美的支持了 ISO C99。但令人遗憾的是,我们的大学老师教给我们的还是老一套,虽然关系不是很大,但这也从侧面反映了我们的教育是多么地滞后!而且我们读的 C 语言书,在不加任何限定的条件下,就说某某语法是不对的,读书的人只能很痛苦地记下!小小吐槽一下,下面继续...

这种变长数组有什么好处呢?你可以使用 alloca 函数达到类似的动态分配数组的效果,但 alloca 函数分配的空间在函数退出时还依然存在,你需要手动地去释放所分配的空间  alloca 函数用来在栈上分配空间,当函数返回时自动释放,无需手动再去释放;VLA 就不一样了,在数组名生命周期结束之后,所分配的空间也就随之释放。

当然,关于 VLA 还有很多限制,例如 ISO/IEC9899 给出了下面这个例子:

extern int n;
int A[n]; // invalid: file scope VLA
extern int (*p2)[n]; // invalid: file scope VM
int B[100]; // valid: file scope but not VM
void fvla(int m, int C[m][m]); // valid: VLA with prototype scope
void fvla(int m, int C[m][m]) // valid: adjusted to auto pointer to VLA
{
typedef int VLA[m][m]; // valid: block scope typedef VLA
struct tag {
int (*y)[n]; // invalid: y not ordinary identifier
int z[n]; // invalid: z not ordinary identifier
};
int D[m]; // valid: auto VLA
static int E[m]; // invalid: static block scope VLA
extern int F[m]; // invalid: F has linkage and is VLA
int (*s)[m]; // valid: auto pointer to VLA
extern int (*r)[m]; // invalid: r has linkage and points to VLA
static int (*q)[m] = &B; // valid: q is a static block pointer to VLA
}

至于上面语法的原因,请参考 ISO/IEC9899 。

GCC 中零长数组

GCC 中允许使用零长数组,把它作为结构体的最后一个元素非常有用,下面例子出自 gcc 官方文档。

struct line {
int length;
char contents[0];
}; struct line *thisline = (struct line *) malloc (sizeof (struct line) + this_length);
thisline->length = this_length;

从上例就可以看出,零长数组在有固定头部的可变对象上非常适用,我们可以根据对象的大小动态地去分配结构体的大小。

在 Linux 内核中也有这种应用,例如由于 PID 命名空间的存在,每个进程 PID 需要映射到所有能看到其的命名空间上,但该进程所在的命名空间在开始并不确定(但至少为 init 命名空间),需要在运行是根据 level 的值来确定,所以在该结构体后面增加了一个长度为 1 的数组(因为至少在一个init命名空间上),使得该结构体 pid 是个可变长的结构体,在运行时根据进程所处的命名空间的 level 来决定 numbers 分配多大。(注:虽然不是零长度的数组,但用法是一样的)

struct pid
{
atomic_t count;
unsigned int level;
/* lists of tasks that use this pid */
struct hlist_head tasks[PIDTYPE_MAX];
struct rcu_head rcu;
struct upid numbers[1];
};

参考资料

  • ISO/IEC9899
  • GCC Online Documents

GCC 中零长数组与变长数组的更多相关文章

  1. 快学Scala 第三课 (定长数组,变长数组, 数组循环, 数组转换, 数组常用操作)

    定长数组定义: val ar = new Array[Int](10) val arr = Array("aa", "bb") 定长数组赋值: arr(0) = ...

  2. C99中的变长数组(VLA)

    处理二维数组的函数有一处可能不太容易理解,数组的行可以在函数调用的时候传递,但是数组的列却只能被预置在函数内部.例如下面这样的定义: #define COLS 4 int sum3d(int ar[] ...

  3. PL/SQL — 变长数组

    PL/SQL变长数组是PL/SQL集合数据类型中的一种,其使用方法与PL/SQL嵌套表大同小异,唯一的区别则是变长数组的元素的最大个数是有限制的.也即是说变长数组的下标固定下限等于1,上限可以扩展.下 ...

  4. 变长数组(variable-length array,VLA)(C99)

    处理二维数组的函数有一处可能不太容易理解,数组的行可以在函数调用的时候传递,但是数组的列却只能被预置在函数内部.例如下面这样的定义: #define COLS 4 int sum3d(int ar[] ...

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

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

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

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

  7. C++内存分配及变长数组的动态分配

    //------------------------------------------------------------------------------------------------ 第 ...

  8. C99新特性:变长数组(VLA)

    C99标准引入了变长数组,它允许使用变量定义数组各维.例如您可以使用下面的声明: ; ; double sales[rows][cols]; // 一个变长数组(VLA) 变长数组有一些限制,它必须是 ...

  9. [改善Java代码]若有必要,使用变长数组

    Java中的数组是定长的,一旦经过初始化声明就不可改变长度,这在实际使用的时候非常不方便.比如要对一个班级的学生信息进行统计,因为我们不知道班级会有多少个学生(随时可能有退学,入学,转学),所以需要一 ...

随机推荐

  1. Windows Server 2008 R2 安装 media Service 部分更新没有安装 KB963697

    1.下载安装包 Windows6.1-KB963697-x64.msu 2.安装过程失败,提示未安装更新 我是阿里云的服务器,一直出现下面提示 经过反复研究,确定系统问题,重新恢复了初始系统,再进行安 ...

  2. 如何使用Dubbo服务和集成Spring

    Dubbo是什么? Dubbo是阿里巴巴SOA服务化治理方案的核心框架,每天为2,000+个服务提供3,000,000,000+次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点. Dubbo是一个 ...

  3. html页面内容超出后显示水平滚动条的问题

    这个问题已经遇到好几次,解决起来也熟练了很多.   出现这种问题一般都是html或页面中的某一内部元素宽度超了. 下面总结我遇到的几种情况: 1.某一内部元素width设为100%,然而它还有bord ...

  4. HttpClient通过Post上传多个文件

    public static String sendFilesPost(String url, String fileNames) { HttpClient httpClient = null; Htt ...

  5. 【Java学习系列】第1课--Java环境搭建和demo运行

    本文地址 分享提纲: 1. java环境的搭建 2. java demo代码运行 3.参考文档 本人是PHP开发者,一直感觉Java才是程序的王道(应用广,科班出身),所以终于下决心跟一跟. 主要是给 ...

  6. ASP.NET MVC搭建项目后台UI框架—6、客户管理(添加、修改、查询、分页)

    目录 ASP.NET MVC搭建项目后台UI框架—1.后台主框架 ASP.NET MVC搭建项目后台UI框架—2.菜单特效 ASP.NET MVC搭建项目后台UI框架—3.面板折叠和展开 ASP.NE ...

  7. GJM : Unity调用系统窗口选择本地文件

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...

  8. 十一个行为模式之策略模式(Strategy Pattern)

    定义: 定义一系列的算法,将每一个算法封装起来,并使它们之间可以相互替换,让算法具有可扩展性和对立性. 结构图: Context:环境类,算法的使用者.对外提供了算法使用的接口,并且持有一个抽象算法类 ...

  9. XML序列化和反序列化

    上篇总结了下JSON的序列化和反序列化,博园中大牛给了很多牛叉的评论,学习了不少. 不过在上篇中忘了把json序列化和反序列化的另外一种方式写上去了,这里做个简单的补充: Json篇:http://w ...

  10. 又到周末了,我们一起来研究【浏览器如何检测是否安装app】吧

    前言 扯淡 这个月比较倒霉,我送了女朋友一台笔记本电脑作为生日礼物,结果15天一过电脑就坏了,悲剧的我还把电脑盒子给扔了!淘宝不给换更不给退 于是被女朋友臭骂了一过星期后,今天本来在公司有任务的,但是 ...