C语言动态数组

    动态数组是指在声明时没有确定数组大小的数组,即忽略圆括号中的下标;当要用它时,可随时用ReDim语句(C语言中用malloc语句)重新指出数组的大小。使用动态数组的优点是可以根据用户需要,有效利用存储空间。

  动态数组,是相对于静态数组而言。静态数组的长度是预先定义好的,在整个程序中,一旦给定大小后就无法改变。而动态数组则不然,它可以随程序需要而重新指定大小。动态数组的内存空间是从堆(heap)上分配(即动态分配)的。是通过执行代码而为其分配存储空间。当程序执行到这些语句时,才为其分配。程序员自己负责释放内存。(欲详细了解堆请见堆栈) 

为什么要使用动态数组?

  在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定。对于这种问题,用静态数组的办法很难解决。为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数结合指针可以按需要动态地分配内存空间,来构建动态数组,也可把不再使用的空间回收待用,为有效地利用内存资源提供了手段。 

动态数组与静态数组的对比

  对于静态数组,其创建非常方便,使用完也无需释放,要引用也简单,但是创建后无法改变其大小是其致命弱点! 

  对于动态数组,其创建麻烦,使用完必须由程序员自己释放,否则严重会引起内存泄露。但其使用非常灵活,能根据程序需要动态分配大小。 

  如何构建动态数组 

遵循原则

  申请的时候从外层往里层,逐层申请; 

  释放的时候从里层往外层,逐层释放。 

构建所需指针

   对于构建一维动态数组,需要一维指针;对于二维,则需要一维,二维指针;三维需要一,二,三维指针;

依此类推。 

构建所需函数

   

函数原型                                   返 回                                         功能说明

void *malloc(unsigned int size);                  成功:返回所开辟空间首地址 失败:返回空指针  向系统申请size字节的堆空间 

void *calloc(unsigned int num, unsigned int size);成功:返回所开辟空间首地址 失败:返回空指针  按类型申请numsize字节的堆空间 

void free(void *p);                               无返回值                                  释放p指向的堆空间 

void *realloc(void *p,unsigned int size);         成功:返回新开辟空间首地址 失败:返回空指针 p指向的堆空间变为size

说明:

  (1)规定为void *类型,这并不是说该函数调用后无返回值,而是返回一个结点的地址,该地址的类型为void(无类型或类型不确定),即一段存储区的首址,其具体类型无法确定,只有使用时根据各个域值数据再确定。可以用强制转换的方法将其转换为别的类型。例如:

double *pd=NULL; pd=(double *)calloc(10,sizeof(double)); 表示将向系统申请10个连续的double类型的存储空间,并用指针pd指向这个连续的空间的首地址。并且用(double)calloc( )的返回类型进行转换,以便把double类型数据的地址赋值给指针pd。 

  (2)使用sizeof的目的是用来计算一种类型的占有的字节数,以便适合不同的编译器。 

    (3)由于动态分配不一定成功,为此要附加一段异常处理程序,不致程序运行停止,使用户不知所措。

通常采用这样的异常处理程序段: 

         if(p==NULL) /* 或者if!p*/  

           { 

             printf("动态申请内存失败!\n"); 

             exit(1); //异常退出

           } 

  (4)这四个函数头文件均包含在<stdlib.h>中。 

  (5)分配的堆空间是没有名字的 只能通过返回的指针找到它。 

  (6)绝不能对非动态分配存储块使用free。也不能对同一块内存区同时用free释放两次。 

  如:free(p);free(p); 

   (7)调用 free() 传入指针指向的内存被释放但调用函数的指针值可能保持不变因为p是作为形参而传递给了函数。严格的讲被释放的指针值是无效的因为它已不再指向所申请的内存区。这时对它的任何使用便可能会可带来问题。 

  

    malloccalloc的区别

  对于用malloc分配的内存区间,如果原来没有被使用过,则其中的每一位可能都是0;反之,如果这部分内存空间曾经被分配、释放和重新分配,则其中可能遗留各种各样的数据。也就是说,使用malloc()函数的程序开始时(内存空间还没有被重新分配)能正常运行,但经过一段时间后(内存空间已被重新分配)可能会出现问题,因此在使用它之前必须先进行初始化(可用memset函数对其初始化为0),但调用calloc()函数分配到的空间在分配时就已经被初始化为0了。 

  当你在calloc()函数和malloc()函数之间作选择时,你需考虑是否要初始化所分配的内存空间,从而来选择相应的函数。 

具体构建方法

    以三维整型数组array[n1][n2][n3]为例。 

先遵循从外层到里层,逐层申请的原则: 

最外层指针是array,它是个三维指针,所指向的是array[],其为二维指针。

所以给array(三维指针) 申请内存应: 

  array=(int***)calloc(n1,sizeof(int**)); 

次层指针是array[],它是个二维指针,所指向的是array[][],其为一维指针。

所以给array[](二维指针)申请内存应: 

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

    

      array[i]=(int**)calloc(n2,sizeof(int*)); 

    

最内层指针是array[][],它是个一维指针,所指向的是array[][][],其是个整型常量。

所以给array[][](一维指针)申请内存应: 

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

    

      for(j=0;j<n2;j++) 

        

          array[i][j]=(int*)calloc(n3,sizeof(int)); 

        

    }   array[][][](整型常量) 

  当然,你可以把它们整合在一起为: 

  int i,j,k; 

  int n1,n2,n3; 

  int ***array; 

  scanf("%d%d%d",&n1,&n2,&n3); 

  array=(int***)calloc(n1,sizeof(int**)); 

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

    

      array[i]=(int**)calloc(n2,sizeof(int*)); 

      for(j=0;j<n2;j++) 

        

          array[i][j]=(int*)calloc(n3,sizeof(int)); 

          for(k=0;k<n3;k++) 

            

              array[i][j][k]=i+j+k+1; 

            

        

    

  最后不要忘了释放这些内存,这要遵循释放的时候从里层往外层,逐层释放的原则。 

  分析过程可参考上面的解答,这里不再赘述。只给出代码吧: 

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

    

      for(j=0;j<n2;j++) 

      

        free(array[i][j]);//释放第一维指针 

      

     

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

    

      free(array[i]);//释放第二维指针 

    

  free(array);//释放第三维指针 

  其余维的如四维创建过程大同小异,这里不再赘述。 

基础概念

  C#集合、C#动态数组的概念之集合,什么是集合呢?集合就如同数组,用来存储和管理一组特定类型的数据对象,除了基本的数据处理功能,集合直接提供了各种数据结构及算法的实现,如队列、链表、排序等,可以让你轻易地完成复杂的数据操作。在使用数组和集合时要先加入system.collections命名空间,它提供了支持各种类型集合的接口及类。集合本身上也是一种类型,基本上可以将其作为用来存储一组数据对象的容器,由于c#面向对象的特性,管理数据对象的集合同样被实现成为对象,而存储在集合中的数据对象则被称为集合元素。这里提到了接口这个概念,它也是面向对象编程进化的重要标准,我们在这里不做过多的讲解,先注重学习集合中的对象及其使用就可以了,下面我们来学习第一种集合: 

  C#集合、C#动态数组的概念之C#动态数组ArrayList.ArrayList 类提供了继承了IList接口。什么是继承呢?这也是面向对象语言的重要特点之一,现在你们先把它理解为,如果一个对象继承了类或接口,那么它也具有了这个类和接口中的方法、属性,可以用这些继承的方法和属性来做相应的操作,比如:数组增加元素没有Add()方法,但是动态数组ArrayList继承了一个增加元素有Add()方法的接口,那么当它要增加元素的时候,不仅可以用索引,也可以用继承下来的Add()方法了。随着学习的深入,我会给大家再具体讲解继承的概念和使用继承的好处。那么下面让我们来看看动态数组所继承的这个接口IList它有什么特性呢? 

  C#动态数组之 Ilist接口:定义了利用索引访问集合对象的方法,还继承了ICollectionIEnumerable接口,除实现了接口原有的方法成员外,其本身也定义多个专门的方法成员,例如新增、移除、在指定位置插入元素或是返回特定元素在集合中所在的位置索引,这些方法主要为集合对象提供类似数组的元素访问功能。 

  C#动态数组之ILsit接口成员:addinsertRemoveAtRemovecontainsClearindexof方法,它最大的特色在于提供类似数组索引的访问机制。 

  C#集合、C#动态数组的概念的基本情况就向你介绍到这里,希望对你了解和学习C#集合、C#动态数组的概念有所帮助。 

必看知识

  由于百科里贴的代码每行前都会有许多中文空格,造成直接复制粘贴到编译器上编译时会出现许多错误。(除非自己手工把代码前的空格全删掉) 

  所以我特地把代码贴到了扩展资料那,要的话去那拿吧。 

构建实例

一维

  #include <stdio.h> 

  #include <stdlib.h> 

  int main() 

  

    int n1,i; 

    int *array; 

    puts("输入一维长度:"); 

    scanf("%d",&n1); 

    array=(int*)malloc(n1*sizeof(int));//第一维 

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

    

      array[i]=i+1; 

      printf("%d\t",array[i]); 

    

    free(array);//释放第一维指针 

    return 0; 

  

二维

  #include <stdlib.h> 

  #include <stdio.h> 

  int main() 

  

    int n1,n2; 

    int **array,i,j; 

    puts("输入一维长度:"); 

    scanf("%d",&n1); 

    puts("输入二维长度:"); 

    scanf("%d",&n2); 

    array=(int**)malloc(n1*sizeof(int*)); //第一维 

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

    

      array[i]=(int*)malloc(n2* sizeof(int));//第二维 

      for(j=0;j<n2;j++) 

      

        array[i][j]=i+j+1; 

        printf("%d\t",array[i][j]); 

      

      puts(""); 

    

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

    

      free(array[i]);//释放第二维指针 

    

    free(array);//释放第一维指针 

    return 0; 

  

三维

  #include <stdlib.h> 

  #include <stdio.h> 

  int main() 

  

    int n1,n2,n3; 

    int ***array; 

    int i,j,k; 

    puts("输入一维长度:"); 

    scanf("%d",&n1); 

    puts("输入二维长度:"); 

    scanf("%d",&n2); 

    puts("输入三维长度:"); 

    scanf("%d",&n3); 

    array=(int***)malloc(n1*sizeof(int**));//第一维 

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

    

      array[i]=(int**)malloc(n2*sizeof(int*)); //第二维 

      for(j=0;j<n2;j++) 

      

        array[i][j]=(int*)malloc(n3*sizeof(int)); //第三维 

        for(k=0;k<n3;k++) 

        

          array[i][j][k]=i+j+k+1; 

          printf("%d\t",array[i][j][k]); 

        

        puts(""); 

      

      puts(""); 

    

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

    

      for(j=0;j<n2;j++) 

      

        free(array[i][j]);//释放第三维指针 

      

    

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

    

      free(array[i]);//释放第二维指针 

    

    free(array);//释放第一维指针 

    return 0; 

  

四维

  #include <stdlib.h> 

  #include <stdio.h> 

  int main() 

  

    int n1,n2,n3,n4; 

    int ****array; 

    int i,j,k,m; 

    puts("输入一维长度:"); 

    scanf("%d",&n1); 

    puts("输入二维长度:"); 

    scanf("%d",&n2); 

    puts("输入三维长度:"); 

    scanf("%d",&n3); 

    puts("输入四维长度:"); 

    scanf("%d",&n4); 

    array=(int****)malloc(n1*sizeof(int***));//第一维 

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

    

      array[i]=(int***)malloc(n2*sizeof(int**)); //第二维 

      for(j=0;j<n2;j++) 

      

        array[i][j]=(int**)malloc(n3*sizeof(int*)); //第三维 

        for(k=0;k<n3;k++) 

        

          array[i][j][k]=(int*)malloc(n4*sizeof(int));//第四维 

          for(m=0;m<n4;m++) 

          

            array[i][j][k][m]=i+j+k+m+1; 

            printf("%d\t",array[i][j][k][m]); 

          

          puts(""); 

        

        puts(""); 

      

      puts(""); 

    

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

    

      for(j=0;j<n2;j++) 

      

        for(k=0;k<n3;k++) 

        free(array[i][j][k]);//释放第四维指针 

      

    

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

    

      for(j=0;j<n2;j++) 

      

        free(array[i][j]);//释放第三维指针 

      

    

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

    

      free(array[i]);//释放第二维指针 

    

    free(array);//释放第一维指针 

    return 0; 

  

数组案例

  #include <stdio.h> 

  #include <stdlib.h> 

  int main() 

  

    int*n,*p; 

    int i; 

    n=(int*)calloc(1,sizeof(int)); 

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

    

      n[i]=i+1; 

      printf("%d\t",n[i]); 

      p=(int*)realloc(n,(i+2)*sizeof(int));//动态扩充数组 

      if(p!=NULL) 

        n=p; 

      else 

      

        puts("error!"); 

        return 0; 

      

    

    free(n); 

    return 0; 

  

游戏应用

预备知识

  (1getch() 

  函数原型: int getch(void); 

  函数功能: 从控制台读取一个字符,但不显示在屏幕上。 

  函数返回: 读取的字符。 

  (2rand() 

  函数原型: int rand(void); 

  函数功能随机函数, 产生032767间的随机整数(00x7fff之间)。 

  函数返回随机整数 

  所属文件: <stdlib.h> 

  (3srand() 

  函数原型: void srand(unsigned seed); 

  函数功能该函数和rand随机函数配合使用,产生随机数的起始发生数据。 

  参数说明: seed为无符号整数。 

  所属文件: <stdlib.h> 

  (4time() 

  函数原型: time_t time(time_t *timer) 

  函数功能得到机器的日历时间或者设置日历时间。 

  函数返回机器日历时间。 

  参数说明: timer=NULL时得到机器日历时间,timer=时间数值时,用于设置日历时间, 

  time_t是一个long类型。 

  所属文件: <time.h> 

  (5'\b'实现退格,即当前光标后退一格。 

  (6'\a'实现响铃,即执行时计算机会嘟一声。 

  (7)得到随机数值范围在a~b(包含b)的方法:rand()%(b-a+1)+a; 

代码

  #include <stdio.h> 

  #include <conio.h>//getch()函数所需头文件 

  #include <stdlib.h>//随机函数所需头文件 

  #include <time.h>//time()函数所需头文件 

  

    void clear(void)//此函数用以清除当前行 

  

    printf("\r \r"); 

  

  

    void start(void) 

  

    puts("这是消单词游戏的精简版,还不会图像编程的人可以看一看"); 

    puts(" 由于没有引人数据库,所以此单词是模拟的。"); 

    puts("按任意键开始,Esc键结束,tab键重新开始:"); 

    puts(" 一旦输错,将发出声音警告,你必须重新输入。"); 

    puts(" 按任意键开始,Esc键结束:"); 

    if( '\x1b' == getch() ) //Esc键结束 

    

      exit( 1 ); 

    

  

  

    int main() 

  

    char*c_rand,*c_input; 

    int i,j,N,n,space,N_rand; 

    start(); 

    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值