专栏首页休辞醉倒浅析变长数组(VLA)和动态数组

浅析变长数组(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函数可能会使内存块移动到了其他地方。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Ext中 get、getDom、getCmp的区别

    getDom方法能够得到文档中的DOM节点,该方法中包含一个参数,该参数可以是DOM节点的id、DOM节点对象或DOM节点对应的Ext元素(Element)等。...

    休辞醉倒
  • Eclipse编码问题

    一直以来都以为eclipse设置好workspace与jsp的编码就万事大吉了,今天编辑properties文件时突然出现了问题,究其原因就是编码默认为ISO-...

    休辞醉倒
  • 关于nohup在连接断开后进程退出的小记

    工作中遇到了迁移数据库的操作,sql文件30G,当然不能搞到本地用Navicat导入,要通过命令导入。

    休辞醉倒
  • 最新TensorFlow能碾压PyTorch?两大深度学习框架最全比拼

    TensorFlow是毫无争议的深度学习框架重量级冠军,而PyTorch是年轻的后起之秀,赢的了不少人的青睐。

    磐创AI
  • MongoDB 如何使用内存?为什么内存满了?

    Mongod 进程启动后,除了跟普通进程一样,加载 binary、依赖的各种library 到内存,其作为一个DBMS,还需要负责客户端连接管理,请求处理,数据...

    MongoDB中文社区
  • MongoDB 如何使用内存?为什么内存满了?

    Mongod 进程启动后,除了跟普通进程一样,加载 binary、依赖的各种library 到内存,其作为一个DBMS,还需要负责客户端连接管理,请求处理,数据...

    MongoDB中文社区
  • NUMBER长度的误解

    NUMBER类型是Oracle的一种变长数值类型,他的取值范围是10^(-130)-10^126(不包括),精度是38位,存储空间是1-22字节。

    bisal
  • 小侃 SQL加密和性能

    细说SQL Server中的加密 简介 加密是指通过使用密钥或密码对数据进行模糊处理的过程。在SQL Server中,加密并不能替代其他的安全设置,比如...

    逸鹏
  • POJ 3694 Network(Tarjan求割边+LCA)

    Description A network administrator manages a large network. The network consist...

    attack
  • LeetCode 115 Distinct Subsequences

    Given a string S and a string T, count the number of distinct subsequences of S ...

    ShenduCC

扫码关注云+社区

领取腾讯云代金券