10.8 变长数组(VLA)

处理二维数组的函数有一处可能不太容易理解:数组的行可以在函数调用时传递,但是数组的列却只能被预置在函数内部。例如下面这样的定义:

阅读 ‧ 电子书库

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

现在,假设定义了如下的数组:

阅读 ‧ 电子书库

可以使用下面的函数调用:

阅读 ‧ 电子书库

这是因为行数可以传递给参量rows,而rows是一个变量。但是如果要处理6行5列的数组,则需要创建另一个新的函数,其COLS定义为5。这是由于数组的维数必须是常量;因此不能用一个变量来代替COLS。

创建一个处理任意二维数组的函数,也是有可能的,但是比较繁琐(因为这样的函数需要把数组当作一维数组传递,然后由函数计算每行的起始地址)。而且,这种技巧和FORTRAN语言子程序不太一致,FORTRAN语言允许在函数调用中指定二维的大小。虽然FORTRAN是很古老的编程语言,但多年以来,数值计算专家们研究出了很多有用的FORTRAN计算库。C正在逐渐代替FORTRAN,因此如果能够简单地转换现有的FORTRAN库将是很有益处的。

出于上面的原因,C99标准引入了变长数组,它允许使用变量定义数组各维。例如您可以使用下面的声明:

阅读 ‧ 电子书库

正如前面提到的,变长数组有一些限制。变长数组必须是自动存储类的,这意味着它们必须在函数内部或作为函数参量声明,而且声明时不可以进行初始化。

阅读 ‧ 电子书库 变长数组的大小不会变化
变长数组中的“变”并不表示在创建数组后,您可以修改其大小。变长数组的大小在创建后就是保持不变的。“变”的意思是说其维大小可以用变量来指定。

因为变长数组是新增的特性,所以目前支持它的并不多。让我们来看一个简单的例子,该例阐明了如何编写一个计算任意二维int数组的和的函数。

首先,下面的代码示范了如何声明带有一个二维变长数组参数的函数:

阅读 ‧ 电子书库

请注意前两个参量(rows和cols)用作数组参量ar的维数。因为ar的声明中使用了rows和cols,所以在参量列表中,它们两个的声明需要早于ar。因此,下面的原型是错误的:

阅读 ‧ 电子书库

C99标准规定,可以省略函数原型中的名称;但是如果省略名称,则需要用星号来代替省略的维数:

阅读 ‧ 电子书库

第二,函数的定义如下:

阅读 ‧ 电子书库

除了新的函数头之外,这个函数区别于古典C(请参见程序清单10.17)的地方就是用变量cols代替常量COLS。这是因为在函数头中使用了变长数组。由于使用了代表行数和列数的两个变量,使得我们能够使用这个新的sum2d()函数处理任意的二维int数组。从程序清单10.18中可以看出来这点。但是,前提是编译器必须能够支持变长数组这个新特性。该程序也说明基于变长数组的函数既可以处理古典C数组也可以处理变长数组。

程序清单10.18 vararr2d.c程序

阅读 ‧ 电子书库

阅读 ‧ 电子书库

需要注意的一点是,函数定义参量列表中的变长数组声明实际上并没有创建数组。和老语法一样,变长数组名实际上是指针,也就是说具有变长数组参量的函数实际上直接使用原数组,因此它有能力修改做为参数传递进来的数组。下面程序段中指出了指针是何时声明的,以及实际数组是何时声明的。

阅读 ‧ 电子书库

如程序所示,当调用twoset()时,ar成为指向thing[0]的指针,并创建10x6的数组temp。由于ar和thing都是指向thing[0]的指针,因此ar[0][0]和thing[0][0]也是同一个数据。

变长数组允许动态分配存储单元。这表示可以在程序运行时指定数组的大小。常规的C数组是静态存储分配的,也就是说数组大小在编译时已经确定。这是因为数组大小是常量,所以编译器可以得到这些信息。第12章将详细介绍动态存储单元分配。