9.7 变量长度数组——VLA(C99)

9.7.1 简述

C99增加了一种新的数组——变量长度数组(VLA, Variable Length Array),这种数组允许在程序执行时才确定数组的大小。即这种数组的尺寸不再一定是整数类型常量表达式,可以是任一值大于0的整数类型表达式。

变量长度数组这种数据类型是C89和C++中都没有的数据类型,这种类型在许多问题中都非常有用,尤其是在数值处理方面,它在许多问题解决方案的描述上特别有力而且特别灵活。这可能是为了收编FORTRAN的一些成熟的算法,由于有了这种数据类型,现在许多程序可以写得更有通用性也更流利了。

下面代码是使用“变量长度数组”的一个例子。

程序代码9-24

9.7 变量长度数组——VLA(C99) - 图1

9.7 变量长度数组——VLA(C99) - 图2

运行结果如图9-23所示。

9.7 变量长度数组——VLA(C99) - 图3

图9-23 变量长度数组例一

特别需要说明的是,这种数组只可以是auto类别的局部变量。也就是说,只能在函数内部定义这种数组,而且不可以是static类别的。

既然变量长度数组只能是局部且必须是auto类别的,那么就一定是在某个复合语句模块中定义的。因此程序执行到这个模块的时候这个数组才获得自己的存储空间,而且,和所有auto类别的局部变量一样,程序一旦执行完它所在的复合语句模块,这个数组也就消失了——内存空间还给了操作系统。

有一种翻译把“变量长度数组”称为“可变长数组”。实际上这种数组的长度并不可变,它仅仅是用“变量”(Variable)来说明数组的长度而已。

一旦变量长度数组存在了(进行变量定义之后),就不可以再改变大小,直到最后消亡(离开作用区域)。并不会因为定义其长度的变量值的改变而改变自己的长度,但下次存在可能具有不同于上次的大小。下面代码运行的结果说明了这一点。

程序代码9-25

9.7 变量长度数组——VLA(C99) - 图4

运行结果如图9-24所示。

9.7 变量长度数组——VLA(C99) - 图5

图9-24 变量长度数组例二

以往的sizeof运算都是在编译期间完成的,而且sizeof里的表达式不求值,例如:

9.7 变量长度数组——VLA(C99) - 图6

这个运算在编译时完成,“i”也不会被赋值为“2”,因为编译时“i”可能还不存在。

但是对于C99中的VLA,sizeof需要在程序执行时进行,此时sizeof的运算对象被求值。

C99对变量长度数组有一个限制,这种数据类型不可以作为结构体或联合体的成员。

9.7.2 变量修饰类型(Variably modified type)

变量长度数组的数组名在作为右值的时候同样是一个指向其起始元素的指针。在C99中也允许指向变量长度数组的指针。

同样,在C99中也存在指向变量长度数组的指针,这样的数据类型与变量长度数组统称为变量修饰类型(Variably modified type)。比如,对于变量长度数组

9.7 变量长度数组——VLA(C99) - 图7

来说,“arr”的右值的类型就是指向“int [n]”类型的指针类型“int(*)[n]”,与之相对应的变量可以按照如下方式定义:

9.7 变量长度数组——VLA(C99) - 图8

这里的“p”,和“arr”一样都属于变量修饰类型。

下面代码在语法上演示了这种指针的用法。

程序代码9-26

9.7 变量长度数组——VLA(C99) - 图9

程序输出如图9-25所示。

9.7 变量长度数组——VLA(C99) - 图10

图9-25 变量修饰类型

9.7.3 变量长度数组与函数参数

了解了变量长度数组值的类型,就可以写出以变量长度数组作为实参的函数。以9.7.2小节中的“int d[n][n];”为例,考虑用一个函数判断“d”是否构成一个单位矩阵。

首先,由于“d”作为右值的类型为“int ()[n]”或“int [ ][n ]”,所以在函数原型中对应参数的类型为“int()[n]”或“int [ ][n ]”。这表示一指向变量长度数组的指针。其中的“n”也可以写成其他标识符,如写成“int (*)[k]”。问题在于“n”或“k”这个标识符在使用前必须得到说明,这是C语言的一个原则。所以在此参数类型之前必须有另外一个参数——关于“k”的类型说明。此外,数组第一维度的长度也必须作为参数。这样函数原型就应当写成:

9.7 变量长度数组——VLA(C99) - 图11

与此相对应,函数的定义可以写成下面的形式

9.7 变量长度数组——VLA(C99) - 图12

这样描述的函数定义及函数原型具有更广泛的适用范围,它可以接受任何二维数组作为参数,无论其是否为方阵,也无论其是否为变量长度数组。下面代码给出了这种函数的写法和测试。

程序代码9-27

9.7 变量长度数组——VLA(C99) - 图13

9.7 变量长度数组——VLA(C99) - 图14

测试结果如图9-26所示。

9.7 变量长度数组——VLA(C99) - 图15

图9-26 变量长度数组与函数参数

按照C99标准,“int shi_dwz(int k, int()[k], int);”这样的函数原型也可以不写出“[k]”里的那个“k”,而代之以“”,这样也就无需对“k”的类型进行说明,即把函数原型写成:

9.7 变量长度数组——VLA(C99) - 图16

但是这种格式目前Dev C++尚不支持。

练习

写一个可求两矩阵相乘的函数,并自行测试