浅析变长数组(VLA)和动态数组

前天实训听见几位推免的大佬聊面试中出现了动态数组,而我们所学并没有涉及到动态数组,遂翻起了尘封已久的《C语言程序设计现代方法》以及《C Primer Plus》,果然大神们写书都很全面(厚),后悔当初没有认真拜读。

一、C99中的变长数组

下面程序用到了变长数组

#include<stdio.h>

int main()
{
int i,n;

printf(“How many numbers do you want to reverse?”);
scanf(“%d”,&n);

int a[n]; //只有C99支持n决定数组范围

printf(“Enter %d numbers: “,n);
for(i=0;i<n;i++)
{
scanf(“%d”,&a[i]);
}
printf(“In reverse order:”); //倒序输出数组
for(i=n-1;i>=0;i–)
printf(” %d”,a[i]);
printf(“\n”);

return 0;

}

上面程序中的数组a是一个变长数组(variable-length array,简称VLA)。变长数组的长度是在程序执行时计算的,而不是在程序编译时计算的。变长数组的主要优点是程序员不必在构造数组时随便给定一个长度,程序在执行时可以准确地计算出所需的元素个数。如果让程序员来制定长度,数组可能过(浪费)或过短(导致程序出错)。

变长数组的长度不一定要用变量来指定,任意表达式(可以含运算符)都可以,例如:

int a[3*i+5];

int b[j+k];

现在我们已经知道什么是变长数组了,但是,假如,变长数组作为形式参数,到底应该如何写呢?

现在,假设有一函数

int sum_array( int a[n] , int n )

{

……

}

编译器会在遇到int a[n]时显示出错信息,因为此前它没有见过n。

下面给出正确的函数原型:

int sum_array(int n , int a[n] );    //version 1

int sum_array(int n , int a[*] );    //version  2

一般来说,变长数组形式参数的长度可以是任意表达式。例如,假设我们要编写一个函数来连接两个数组a和b,要求先复制a的元素,再复制b的元素,把结果写入第三个数组c:

int concatenate(int m , int n , int  a[m] , int  b[n] , int c[m+n] )

{

}

数组c的长度是a和b的长度之和。这里用于指定数组c长度的表达式只用到了另外两个参数;但一般来说,该表达式可以使用函数外部的变量,甚至可以调用其他函数。

敲黑板!!!重点来了

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

变长数组允许动态分配存储单元,这表示可以在程序运行时指定数组的大小。常规的C数组是静态存储分配的,也就是说在编译时数组的大小就已经确定。

接下来要说的动态数组,才是大小会变化的数组。

二、动态数组

现在我们讨论C语言中如何实现动态数组。请系好安全带,加速了加速了。

基本思路就是使用malloc()库函数(内存分配)来得到一个指向一大块内存的指针。然后,像引用数组一样引用这块内存,其机理就是一个数组下标访问可以改写为一个指针加上偏移量。

1.使用malloc函数为数组分配存储空间

假设正在编写的程序需要n个整数构成的数组,这里的n可以在程序执行期间计算出来。首先,声明一个指针变量:

int *a;

一旦n的值已知了,就让程序调用malloc函数为数组分配存储空间:

a=malloc( n * sizeof(int) );

一旦a指向动态分配的内存块,就可以忽略a是指针的事实,可以把它作为数组的名字。这都要感谢C语言中数组和指针的紧密关系。例如,可以使用下列循环对a指向的数组进行初始化:

for ( i=0 ; i<n ; i++ )

a[i]=0;

当然,用指针算数运算取代下标操作来访问数组元素也是可行的。

2.使用calloc函数为数组分配存储空间

calloc函数在<stdlib.h>中具有如下所示的原型:

void * calloc ( size_t nmemb , size_t size );

calloc函数为nmemb个元素的数组分配内存空间,其中每个元素的长度都是size个字节。如果要求的空间无效,那么此函数返回空指针。在分配了内存之后,calloc函数会通过把所有位设置为0的方式进行初始化。例如下列calloc函数调用为n个整数的数组分配存储空间,并且保证所有整数初始均为零:

a=calloc(n,sizeof(int));

3.使用realloc函数调整数组的大小

一旦为数组分配完内存,稍后可能会发现数组过大或过小。realloc函数可以调整数组的大小使它更适合需要。<stdlib.h>中的realloc原型:

void * realloc ( void *ptr, size_t size );

当调用realloc函数时,ptr必须指向先前通过malloc、calloc或realloc的调用获得的内存块。size表示内存块的新尺寸,新尺寸可能会小于或大于原有尺寸。虽然realloc不要求ptr指向正在用作数组的内存,但实际上通常是这样的。

在要求减少内存块大小时,realloc函数应该“在原先的内存块上”直接进行缩减,而不需要移动存储在内存块中的数据。同理,扩大内存块时也不应该对其进行移动。如果无法扩大内存块(因为内存块后面的字节已经用于其他目的),realloc函数会在别处分配新的内存块,然后把旧块中的内容复制到新块中。所以,一旦realloc函数返回,一定要对指向内存块的所有指针进行更新,因为realloc函数可能会使内存块移动到了其他地方。

转载于:https://my.oschina.net/shuangquan/blog/1599776

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值