当前位置:网站首页>>电脑技术>>C、C++语言>>我的著作 双击自动滚屏
C语言速成(第四章 数组)

发表日期:2006年1月1日      已经有4641位读者读过此文

第四章  数组

    前面我们已经接触了C语言中数据的一些基本类型,C语言在这些基本类型的基础上还提供了按层次构造起来的构造类型。这些构造类型主要有数组、结构、联合和枚举。多种数据类型的使用使程序设计更有效更灵活。在本章中,重点介绍这些构造类型中的数组。
    数组是大部分高级语言都具有的一种数据类型,对于成组出现的同一数据类型相关数据的处理,数组是一个十分有效的工具。计算机程序语言中使用的数组与数学中的数组概念上基本是一致的。例如我们要处理生产同一种产品的40名工人的生产实绩,不必给每一个人的产量取一个变量名,而只要总的给代表这些工人产量的变量起一个变量名就可以了,这个变量名叫做数组名。在数组名后面必然带有一对或几对方括号,方括号里的数必须是整数,我们称之为数组的下标。C语言的数组下标总是从0开始的,数组通过下标去访问它的每一个成员。
    在数组中每一个成员称为数组的一个元素。数组里的各元素在内存空间中是依次存放的,它们在数组里的位置是由其下标来确定的。当然,构成一个数组的诸元素必需是同一类型的变量,在一个数组里不允许出现不同类型的变量。数组中各元素的值是可变的,数组元素可以象基本类型的变量一样被赋值和在各种表达式里使用。

第一节  一维数组

    只有一个下标的数组元素所组成的数组称为一维数组。一维数组是C语言里经常使用的一类数组。在C语言中,定义一维数组的一般形式为:

        <存储类型> <数据类型> <数组名> [长度];

    例如可以把前面说到的40名工人生产某个产品的数量定义成一个数组:

        int a[40];                     /* 定义一个有40个元素的数组 a */

    方括号里的40表示数组中元素的多少,亦即数组的长度,它必须是一个常量表达式。数组前面的 int 表明数组的元素都是整形变量。这个数组元素的下标值是从0开始到39的整数。引用一维数组元素的一般形式为:

        <数组名> [下标]

    数组 a 的40个元素分别表示为 a[0]、a[1]、a[2]、........ a[38]、a[39],有时候又称这些元素为下标变量。必须注意,在使用数组时不要使下标越界,如对于上面定义的数组就不能使用 a[40],因为已定义的数组中不存在这个成员,使用后可能导致错误和混乱。由此可见,程序设计时必须十分注意确保数组下标在设定的范围内。
    数组的标识符本身还代表该数组的首地址,即指向 a[0] 元素的指针,关于指针的概念我们将在下一章专门讲述。数组一旦被定义后,它所占用的内存空间就确定了。在C语言程序设计中,经常把数组的大小定义成一个符号常量,如把上述数组定义写成:
 
        #define SIZE 40;      /* 通过宏代换命令用符号常量 SIZE 代替常数40 */
        int a[SIZE];          /* 定义一个有40个元素的数组 a */

    和基本类型的变量一样,数组也可以说明其存储类,数组可以说明为自动、外部和静态的存储类。数组若在函数体之外定义,则为外部数组。在函数体内定义的局部数组可以定义成自动数组,也可以定义成一个静态数组,如:

        main()                    /* 主函数 */
        {
           static char s[10];     /* 定义一个有10个元素的字符型静态数组 s */
           int a[10];             /* 定义一个有10个元素的整型数组 a */
           ........
        }

    数组的下标必须是正整数、0、或整形表达式。假如在定义数组的同时,又定义了一个整形变量 i,当把 i 放在数组标识符后的方括号里代替数组下标时,变量 i 取0-39范围内的不同整数,表达式 a[i] 代表数组中相应的元素。下面举例说明数组下标变量和下标的用法。

    例5.1: 输入某纺织厂织布车间甲班40名织布工人当日产量,并计算他们的平均产量。

    #define SIZE 40           /* 通过宏代换命令用符号常量 SIZE 代替常数40 */
    main()                    /* 主函数 */
    {
      int i;                  /* 定义一个自动变量 i */
      float a[SIZE],s=0,p;    /* 定义浮点数组 a、浮点变量 s、p */
      for(i=0;i<SIZE;i++) {   /* 为输入40名工人产量设置的循环 */
        printf("输入第%d名工人织布米数: ",i+1);  /* 屏幕打印输入提示 */
        scanf("%f",&a[i]);    /* 键盘输入第i名工人的产量,赋给相应的数组元素 */
        s+=a[i];              /* 产量累计计算 */
      }
      p=s/SIZE;               /* 计算平均产量 */
      printf("%d名工人平均织布%.2f米",i,p);      /* 屏幕打印平均产量数 */
    }

    数组可以作为函数的参数进行传递。当一维数组作为函数的参数进行传递时,实参和形参均用数组名表示。函数的形参说明时允许省略数组名后方括号中的数组长度,如:

    main()                    /* 主函数 */
    {
      int f[10];              /* 定义一个有10个元素的整型自动数组 f */
      ........
      prog(f);                /* 调用函数 prog,并传递参数 f */
    }

    prog(a)                   /* 定义函数 prog */
    int a[];                  /* 形参说明为一维数组 */
    {
      ........
    }

第二节  字符数组

    字符数组是使用最频繁的数据类型,通常用来存放和处理字符串,它的每个元素存放着一个字符。在C语言里,约定以空字符的转义符号 ''''''''\0'''''''' 作为字符串的结束标志,字符串是由这个空字符之前的字符组成的。由于 ''''''''\0'''''''' 占用了一个字符位置,因此在定义这类数组长度时必须考虑这个因素。例如用于存放字符串“WORLD”的字符数组长度应定义为比该字符串的字符数多一个字符单元,即定义为可容纳6个字符型元素。C语言系统编译时对每一个字符串常量也自动加一个结束符 ''''''''\0'''''''' 。编程时要注意字母与字符串的区别, 在C语言中用 ''''''''A'''''''' 代表字母 A,而用 "A" 表示由 ''''''''A'''''''' 和 ''''''''\0'''''''' 构成的字符串。 在程序中通常通过测定 ''''''''\0'''''''' 来判断字符串是否结束。当然,在编程时并不强求有的要在字符串尾加 ''''''''\0''''''''。字符串数组结束处是否加 ''''''''\0'''''''' 可根据实际需要而定。
    在C语言里,只有字符数组允许对整个数组进行运算和赋值操作,而其他类型数组只能对单个元素进行处理。下面举一个对字符数组进行整体处理的例子:

    例5.2: 从键盘上输入一个字符串,并在屏幕上重显该字符串。
 
    main()                             /* 主函数 */
    {
      char string[50];                 /* 定义一个有50个元素的字符型数组 */
      printf("请输入一个字符串:");     /* 屏幕打印一字符串 */
      scanf("%s",string);              /* 键盘输入字符串 */
      printf("输入的字符串是:%s\n",string); /* 屏幕显示字符串 */
    }

    在上面的程序运行过程中,我们输入字符串结束时,并不需要特别输入一个空字符''''''''\0'''''''',''''''''\0'''''''' 是由C语言在编译时自动加上的。但在对单个字符分开处理时,则往往需要在字符串末尾人为地加上 ''''''''\0'''''''',见例5.3。

    例5.3: 从键盘输入一个字符串,并在屏幕上重显该字符串。

    #include "stdio.h"                 /* 用预处理命令指定包含文件 */
    #define SIZE 50                    /* 通过宏替换命令用符号常量替换常数 */
    main()                             /* 主函数 */
    {
      char string[SIZE];               /* 定义一个字符型数组 */
      int i=0;                         /* 定义一个整型变量 i,并赋初值0 */
      printf("请输入一个字符串:");     /* 屏幕打印提示字符串 */
      for(;;) {                        /* 为逐个输入字符设的循环 */
        string[i]=getchar();           /* 键盘输入一个字符赋给数组相应的元素 */
        if(string[i]==''''''''\n'''''''') break;     /* 如果输入的是回车,退出循环 */
        i++;                           /* 数组下标加1,后移一字符 */
      }
      string[i]=''''''''\0'''''''';                  /* 字符串尾加解束标志 */
      printf("输入的字符串是:%s\n",string);  /* 屏幕打印字符串 */
    }

    在上面的程序中如果不在字符串尾加上 ''''''''\0'''''''',函数 printf 就无法确定字符串到哪里结束了。
    数组可以作为函数的参数进行传递。在调用时传递给函数的是数组第一个元素的地址,即数组的起始地址,而不是整个数组的副本。数组参数的传递实际上是一种指针传递,函数改变了的数组的值,在程序运行退出该函数后仍将被保留下来。关于指针我们将在下一章里再作进一步的介绍。例5.4说明一维字符型数组作为函数参数时的运用方法:

    例5.4: 输入一个字符串到一个数组里,再将它拷贝到另一数组中,最后在屏幕上显示另一数组中的字符串。

    #define SIZE 50                  /* 通过宏替换命令用 SIZE 代替常数50 */
    main()                           /* 主函数 */
    {
      char s1[SIZE],s2[SIZE];        /* 定义字符型数组 s1、s2 */
      printf("请输入一个字符串:");   /* 屏幕打印提示字符串 */
      scanf("%s",s1);                /* 键盘输入一字符串 */
      copy(s1,s2);                   /* 调用函数 copy,实参为 s1、s2 */
      printf("复制的字符串是:%s\n",s2); /* 屏幕打印字符串 */
    }
    copy(char a[],char b[])          /* 定义函数 copy */
    {
      int i=0;                       /* 定义一个整型自动变量 i,赋初值0 */
      while((b[i]=a[i])!=''''''''\0'''''''')  i++; /* 为复制字符串建的循环 */
    }

    主函数 main 中调用了函数 copy,在函数 copy 里通过一个 while 循环从第0号字符起,依次把数组 s1 中的元素复制到数组 s2 里,每循环一次,下标加 1,直到复制完 a 中字符串末的 ''''''''\0'''''''',循环条件不再成立而跳出循环。当然这仅仅作为一个例子,帮助大家学习字符数组编程的方法,实际上在C语言标准函数库里有现成的字符串复制函数,编程者可以直接调用。

第三节 多维数组

    当组成一个数组的每个元素也是数组时,就必须用两个下标来表示它,这样就构成了二维数组。下标个数在两个或两个以上的数组称为多维数组,多维数组可以说是数组的数组,但它的最底层必需是基本类型的。多维数组在使用前也必须先定义,定义多维数组的一般形式为:

     <存储类型> <数据类型> <数组名>[长度1][长度2]……[长度N];

    以下定义多维数组的方式都是合法的:

        int a[3][3][4];                /* 定义一个三维整型数组 a */
        static double b[6][5];         /* 定义二维的双精度浮点数组 b */

    引用多维数组元素的一般表达式为:

     <数组名> [下标] [下标] …… [下标]

    和一维数组一样,多维数组的下标也是从0开始的。如二维数组 t[3][4] 实际上描述了一个3行4列的表(即矩阵),二维数组的两个下标和表中的行、列形成了很自然的对应关系,它的各个元素分别为:

        t[0][0]     t[0][1]     t[0][2]     t[0][3]
        t[1][0]     t[1][1]     t[1][2]     t[1][3]
        t[2][0]     t[2][1]     t[2][2]     t[2][3]

    由于计算机的内存是一维的,所以多维数组在计算机里实际上是转换成一维数组存贮的。上述二维数组各元素在计算机内存中先存放第0行的元素,再依次存放第一行、第二行的元素:

        t[0][0]
        t[0][1]
        t[0][2]
        t[0][3]
        t[1][0]
        t[1][1]
          ∶
          ∶
          ∶
        t[2][2]
        t[2][3]

    我们可以看出,最右边的下标变化速度最快。多维数组对程序设计时描述一些复杂系统的数据结构是非常有用的。例如把一本书的内容放到一个三维数组中,可以定义为:

        char ch[总页数][每页最大行数][每行最大列数];

又如把一个学校学生的成绩列成一个数组可定义为:

        int st[年级数][每年级班级数][每班学生数];
这时候,就可以用 st[3][4][5] 来存放4年级(第一个下标为3)5班(第二个下标为4)第6号学生(第三个下标为5)的成绩。我们用例5.5说明多维数组的用法:

    例5.5:纺织厂某织布车间有6个生产班组,每个班组有5名织布工人,统计这个车间各班组的产量和全车间的产量。

    #define GROUPS 6                   /* 通过宏替换命令用符号常量代替常数 */
    #define WORKES 5                   /* 通过宏替换命令用符号常量代替常数 */
    main()                             /* 主函数 */
    {
      int i,j;                         /* 定义整形自动变量 i、j */
      float a,cloth[GROUPS][WORKES],sum,total=0;  /* 定义浮点变量和数组 */
      for(i=0;i<GROUPS;i++) {          /* 为输入各组数据设的循环 */
        sum=0;                         /* 把0赋给变量 sum */
        for(j=0;j<WORKES;j++)  {       /* 为输入各工人数据设的循环 */
          printf("输入第 %d 组第 %d 号工人织布的米数:",i+1,j+1); /* 显示提示 */
          scanf("%f",&a);              /* 键盘输入数据放入变量 a */
          cloth[i][j]=a;               /* a 的值赋给相应的数组元素 */
          sum+=cloth[i][j];            /* 本组产量累计计算 */
        }
        printf("第 %d 组产量为 %.2f 米\n",i+1,sum); /* 屏幕打印各组产量 */
        total+=sum;                    /* 总产量累计计算 */
      }
      printf("全车间产量为 %.2f 米\n",total);       /* 屏幕打印总产量 */
    }

多维数组也可以作为函数的参数进行传递,这时传递的是多维数组的起始地址。在传递时,作为函数形参的多维数组只允许第一维,也就是最左边的下标不加确定,例如:

        sum(a)                         /* 定义一个函数 sum */
        int a[][10][5];                /* 说明形参为三维的整形数组 */
        {
          ........
        }

    上面函数的形参中,常数10和5都必需是确定的,否则程序编译时就会出错。

第四节  数组的初始化

    数组的初始化也就是给数组的元素赋初值,数组赋初值通常在定义数组时进行,以便在程序编译时就能确定数组元素的初值。C语言规定只能对静态数组和外部数组赋初值,只有字符数组例外,即允许对自动存储类的字符数组赋初值,这是因为字符数组中的字符是作为常量保存的。数组初始化时,既可对数组的全部元素赋初值,也可以只对数组的部分元素赋初值。下面是给一维数组赋初值的例子:

        int a[6]={0,1,2,3,4,5};       /* 定义全局整形数组 a,并赋初值 */
        char b[10]={''''''''W'''''''',''''''''O'''''''',''''''''R'''''''',''''''''L'''''''',''''''''D'''''''',''''''''\O''''''''}; /* 定义字符数组并赋初值 */
        main()                                 /* 主函数 */
        {
          static double c[5]={3.3,20,4.75,6.1,5.0};  /* 定义静态数组并赋初值 */
          .........
        }

    数组初始化时,元素的初值按严格的顺序放在用花括号括起来的初值表中,初值表中的数值间用逗号分隔开。初值表里数值的个数可以少于或等于数组的长度,但不应大于数组的长度,如大于数组长度,多余的初值将不起作用。当少于定义的元素个数时,未出现的元素初值编译程序自动用0填充。初值的数据类型必需与数组类型相一致。当对数组的全部元素赋初值时,说明数组长度的常量表达式可以省略。如上面静态数组 c 赋初值时可隐式地写成:

        static double c[]={3.3,20,4.75,6.1,5.0};   /* 省略数组长度的赋初值法 */

系统自动定义数组的长度为5,它相当于:

        static double c[5];      /* 定义一个有5个元素的双精度浮点静态数组 */
        c[0]=3.3;                /* 给数组第一个元素赋值 */
        c[1]=20;                 /* 给数组第二个元素赋值 */
        c[2]=4.75;               /* 给数组第三个元素赋值 */
        c[3]=6.1;                /* 给数组第四个元素赋值 */
        c[4]=5.0;                /* 给数组第五个元素赋值 */

    字符数组常常用直接在一对双引号中列出字符串的方法进行初始化,如:

        char b[ ]="WORLD";       /* 直接用字符串常量给字符数值赋初值 */

    在第二章里已经介绍过编译程序处理时自动在字符串尾添加一个代表字符串结束标志的空字符 ''''''''\0'''''''',所定义的数组长度等于字符串中的字符数加1。但必须注意,只能在定义一个字符数组时采用这种整体赋初值的办法,在程序的其他地方不允许对字符数组进行这样的整体赋值。因此不允许出现:

        char b[6];
        b[]="WORLD";

    多维数组的初始化可用若干组初值表来赋初值,各组初值表间用逗号隔开,如:

        static int d[3][4]={           /* 多维数组赋初值 */
                      {2,5,1,1},
                      {3,2,8,9},
                      {4,4,5,7}
                   };

上式中第一个下标3也可以省略。在上一节里讲到多维数组在计算机内存中实际上是转换成一维数组存放的,所以上述表达式也可以写成:

        static int d[3][4]={2,5,1,1,3,2,8,9,4,4,5,7};

两种写法是等价的,但我们仍倾向于尽可能用前一种写法,因为它表达比较明晰,有助于程序的阅读。当然也允许只对多维数组的部分元素赋初值,例如:

        static int d[3][4]={           /* 对多维数组部分元素赋初值 */
                          {2},
                          {3,2},
                          {4}
                   };

这时其他元素的值自动置为0,它相当于:

        static int d[3][4]={
                      {2,0,0,0},
                      {3,2,0,0},
                      {4,0,0,0}
                   };




相关专题: 我的著作
专题信息:
  C语言速成(第三章 程序控制语句)(2006-1-1 13:51:28)[9364]
  C语言速成(第七章 联合、枚举和自定义数据类型)(2006-1-1 13:51:46)[9984]
  C语言速成(第六章 结构)(2006-1-1 13:52:05)[8781]
  C语言速成(第五章 指针)(2006-1-1 17:28:54)[4518]
  C语言速成(第二章 常量、变量、运算符与表达式)(2006-1-1 13:53:05)[7207]

相关信息:
 没有相关信息
  打印本页
设为首页 | 加入收藏 | 联系我们 | 管理入口
皖ICP备05018956号
Copyright © 2003 J.W.SHEN All Rights Reserved
后台管理系统 V1.0 制作