现在的位置: 首页 > 编程语言 > 正文

二维数组是什么?C语言二维数组实例分析

2020年02月12日 编程语言 ⁄ 共 3285字 ⁄ 字号 评论关闭

  数学中的行列矩阵,通常使用二维数组来描述,即用二维数组的第一维表示行,第二维表示列;生活中凡是能抽象为对象及对象的若干同类型属性的问题,一般用二维数组来描述。

  例如,若表示一个班级学生的语文、数学、外语、C 语言等 4 门课的成绩数据。该问题可把每个学生看成一个对象,用二维数组的第一维来表示,如果有 50 个学生,则可设定二维数组第一维的大小为 50;成绩可看成每个对象的属性,且均可使用整型表示,可用二维数组的第二维来表示,每个对象(学生)含 4 个属性(4 门课程),故第二维大小可设为 4。

  再比如,某公司若统计某产品的某个月份的销量数据,该问题可以把一周当成一个对象,一个月含 4 周,故 4 个对象,二维数组第一维可设为 4;日销售量可看成每个对象的属性,可用二维数组的第二维表示,对象(每周)含有 7 个属性(7 天的日销售量),故二维数组的第二维可设为 7。

  二维数组的定义

  同一维数组一样,既支持 C89 标准的二维静态数组,又支持 C99 标准的二维动态数组或变长数组。某些 C 编译器还没更新到支持 C99 标准的语法,故可能在一些编译器中变长数组会报错。如无特殊说明,教程中所指二维数组,均默认为静态数组。

  静态二维数组定义的一般格式为:

  类型 数组名[第一维大小][第二维大小];

  其中,第一、二维的大小一般均为常量表达式。

  例如:

  int a[4][5];

  定义了一个 4 行 5 列的 int 型二维数组 a。

  float sc[3][4];

  定义了一个 3 行 4 列的 float 型二维数组 sc。

  如下二维数组的定义形式均是错误的。

  int a[][3];//错误。编译器无法确定所需空间int a[2][];//错误。缺少列下标,编译器无法确定所需空间

  动态数组例子如下(仅做了解)。

  int n=2;int a[n][3];//动态数组,正确的C99语法。但在某些编译器中可能报错int a[2][n];//动态数组,正确的C99语法

  定义时未初始化的数组,其数据元素的值一般为无意义的随机值,如:

  int a[2][3];//该数组的6个元素均为随机值

  可以把二维数组看成一个特殊的一维数组,它的每个元素又是一个一维数组。例如,定义一个表示 3 个学生 4 门课程成绩的二维数组:

  int sc[3][4];

  定义了一个 3 行 4 列的二维数组 sc,该二维数组可表示 3 个对象(学生),从这个角度看,该二维数组可以看成含 3 个对象(学生)的一维数组,3 个对象(元素)分别为:sc[0]、sc[1]、sc[2],其中 sc 为该一维数组名。

  每个对象(元素)sc[i] 又是一个包含 4 个属性(4 门成绩)的一维数组,4 个属性分别 为:sc[i][0](语文)、sc[i][1](数学)、sc[i][2](外语)、sc[i][3](C 语言)。每一行表示一个学生,每一列表示一门课程,形成如下所示的行列矩阵形式。

  语文 数学 外语 c语言

  sc[0](第一个学生)-----sc[0][0] sc[0][1] sc[0][2] sc[0][3]

  sc[1](第二个学生)-----sc[1][0] sc[1][l] sc[1][2] sc[1][3]

  sc[2](第三个学生)-----sc[2][0] sc[2][1] sc[2][2] sc[2][3]

  二维数组名 sc 是首对象(第一个学生)sc[0] 的地址,即 sc=&sc[0]。二维数组名加 1 表示跳过一个对象,即 sc+1 为第二个对象(学生)sc[1] 的地址,sc+2 为第三个对象(学生)sc[2] 的地址。

  从行列式角度分析,二维数组名即首行的地址,C 语言中的地址一般均是空间首地址。故二维数组名是首行首地址,该数组名加 1 表示跳过一整行,到达第二行的首地址,以此类推。

  【例 1】以下二维数组 sc 用于保存 3 个学生的 4 门课程(语数外及 C 语言)成绩。根据程序的运行结果,分析二维数组名的含义。

  #includeint main (void){ int sc[3][4]; printf("sc=%p\n",sc); printf ("&sc[0]=%p\n",&sc[0]); printf("sc+1=%p\n",sc+1); printf("sc+2=%p\n",sc+2); return 0;}

  程序某次运行的结果:

  sc=0060FEE0

  &sc[0]=0060FEE0

  sc+1=0060FEF0

  sc+2=0060FF00

  程序分析:

  1) 本例中定义的二维数组为 3 行 4 列,含 3 个学生对象,对应 3 行,每个学生对象包括 4 个属性,对应 4 列。

  2) 二维数组名 sc 相当于首对象即第一个学生 sc[0] 的地址,即 sc==&sc[0]。输出地址一般使用格式控制符 %p 或 %x 或 %08x。由某次运行结果可知,sc 和 &sc[0] 的值均为十六进制数 0060FEE0。

  由此可得:二维数组名为首对象或首行的地址。

  3) 二维数组名加 1 表示跳过一个对象(一行)的空间,为下一个对象(下一行)的地址。即跳过一个对象所有属性(一行中所有列元素)对应的空间,到达下一个对象(下一行)的起始位置。

  而本例中一个对象有 4 个属性,每个属性均为整型变量,在 VC++6.0 中占 4 字节,故一个对象(一行)共占 4X4=16 个字节。故 sc、sc+1、sc+2 均相差 16 个字节,转换为十六进制为 0x10。如运行结果所示。

  4) 程序每次运行为某变量空间分配的起始地址可能有差别。

  二维数组的引用

  二维数组的引用格式为:

  数组名[行下标][列下标];

  注意引用数组元素时不能加类型,行下标、列下标均从 0 开始。且行下标和列下标的形式可以为常量、变量或表达式。

  若 M 和 N 均为已定义的正整数:

  int i,j; //i和j分别表示行下标和列下标.int a[M][N]; //定义了一个M行N列的二维整型数组a

  行下标i的范围为 0〜M-1。

  列下标j的范围为 0〜N-1。

  称第几个时,习惯上是从第 1 个开始,第 2 个,第 3 个,…,而不从第 0 个开始。但是二维数组的行下标及列下标均是从 0 开始的,为统一起见,本书中对数组元素的引用,采用行列序号来描述,如 a[i][j] 为 i 行 j 列元素,而不说成第 i 行第 j 列元素。例如:

  int a[3][4];a[0][0]; //为0行0列元素a[2][1]; //为2行1列元素a[1][1+2]; //为1行3列元素

  例如:

  int n=4, i, j ;int a[3][4]; //定义了一个3行4列的二维数组a

  对该数组的引用形式为 a[i][j],其中,行下标 i 的范围为 0〜2,列下标 j 的范围为 0〜3。以下对该二维数组的引用均是正确的。

  a[2][n-1]=a[0][1+1] +a[2][2-1]; //a[0][2]、a[2][1]相加后赋给a[2][3]a[3-1][n-1];//引用2行3列元素

  以下对该二维数组的引用形式是错误的。

  a[-2][1]; //行下标-2越界,应为0〜2a[3][2]; //行下标3越界,应为0〜2a[2][n]; //列下标n=4越界,应为0〜3a[2,3]; //引用形式错误,应为a[2][3]a[2][]; //缺少列下标int a[1][n-2]; //引用不能加类型,若为定义,第二维含有变量n,错误

  【例 2】定义一个 2 行 3 列的二维数组,从键盘上输入 6 个数值,依次给该二维数组的每个元素赋值,按每行三个元素输出该二维数组及所有元素的和。

  程序分析:

  例题涉及对二维数组的赋值,一般使用双重循环,外层循环控制行下标,内层循环控制列下标。二维数组的每个元素 a[i][j] 像普通变量一样使用,使用 scanf 函数输入时,一定要加 &。输入时,可以输入完 6 个数据后按一次回车键。

  本题要求每输出一行后换行,即输出完一行中的所有列数据(内层循环结束)后换行。

  实现代码:

  #includeint main (void){ int a[2][3]; //先定义,后赋值 int i,j,s=0; printf("Input 6 integers:"); for(i=0;i<2;i++) { for (j=0; j<3; j++) { scanf ("%d",&a[i][j]); //勿忘 & s+=a[i][j] ; //与上一条语句不能颠倒 } } for(i=0;i<2;i++) { for (j=0; j<3; j++) printf("%d\t",a[i][j]); //使用\t 的作用 printf ("\n"); //注意该输出换行符的位置 } printf("s=%d\n", s); return 0;}   运行结果为:   Input 6 integers:1 2 3 4 5 6   1 2 3   4 5 6   s=21   二维数组的初始化

  二维数组可以先定义,后赋值,在显式赋值之前,二维数组的各数据元素是随机值。也可以在定义二维数组的同时,采用初始化列表的形式对其元素赋初值,称为二维数组的初始化。

  二维数组的初始化方式通常有以下几种。

  1) 分行给出初始化数据,且每行的初始化数据个数等于列数。例如:

  int a[2][3]={{1,2,3},{4,5,6}};

  该初始化列表给出了两行数据,每一行数据用一对大括号 {} 括起来,一行中的数据及行与行之间均用逗号隔开。这是一种较常用的二维数组的初始化方式。

  该初始化语句相当于如下 6 条赋值语句。

  a[0][0]=1; a[0][1]=2; a[0][2]=3;a[1][0]=4; a[1][1]=5; a[1][2]=6;

  由于初始化列表中明确给出了两行数据,故定义该数组时,其第一维的大小可省略,编译器能间接算出该数组的行数为 2,故依然可以确定其空间大小,因此,在对二维数组进行初始化时,其第一维的大小可以省略,即写成如下形式:

  int a[][3]={{l,2,3},{4,5,6}};

  注意:第二维的大小一定不能省略!如下初始化均是错误的。

  int a[2][] = {{l,2,3},{4,5,6}}; //错误。不能省略第二维大小int a[][] = {{l,2,3}, {4,5,6}}; //错误。不能省略第二维大小int a[][3]; //错误。没有提供初始化列表时,两维的大小都必须显式给出int a[2][3] = {{l,2,3},{4,5,6},{7,8,9}}; //错误。初始行数多于数组行数

  如果把上面一条初始化语句改为省略第一维的大小,便是正确的,即:

  int a[][3] = {{1,2,3}, {4,5,6}, {7,8,9}}; //正确。间接得知该数组为三行

  2) 分行给出初始化数据,但每行的初始化数据个数少于列数。例如:

  int a[2][3]={{l,2},{4}};

  该初始化列表给出了两行数据,第一行给出两个数据,少一个,对 int 型默认为补 0;第二行仅给出一个数据,少两个,补两个 0。所以上述初始化语句相当于:

  int a[2][3]={{1,2,0},{4,0,0}};

  同理:

  int a[][3]={{0},{0}};

  相当于:

  int a[][3] = {{0,0,0},{0,0,0}}; //2行3列

  3) 初始化数据没有分行,容易产生混乱,不推荐这种方式。

  如果初始化数据的个数是列数的整数倍,即:

  int a[2][3]={l,2,3,4,5,6};

  初始化数据以列数三个为一组,共分为两组,且每组数据个数恰好等于列数 3,故第一组赋值给第 1 行,第二组赋值给第 2 行。初始化后,数组中各元素为:

  1 2 3

  4 5 6

  例如:

  int a[2][3]={1,2,3,4};

  由于该二维数组列数为 3,初始化数据以三个为一组,共分为两组,但第二组仅一个数据,少于列数 3,对 int 型数组用 0 补齐。故第一组数据 1,2,3 赋值给第一行,第二组补齐为三个数据 4,0,0 后,赋值给第二行。相当于 int a[][3]={1,2,3,4,0,0}; 初始化后, 数组中各元素为:

  1 2 3

  4 0 0

  int a[][3] = {1,2,3,4,5,6,7,8}; //正确,可间接得知该数组为三行,不推荐

  第三行不够三个数据,用 0 补齐。故该语句相当于:

  int 3[][3] = {1,2,3,4,5,6,7,8,0};

  初始化后,数组中各元素为:

  1 2 3

  4 5 6

  7 8 0

  如果第一维大小没有省略,则初始化数据的个数一定不能超过数组元素的总个数,否则报错。例如:

  int a[2][3] = {1,2,3,4,5,6,7,8};//错误。初始数据个数8多于数组总个数6

  二维数组的存储

  二维数组在逻辑(表现形式)上可理解为矩阵形式(分行分列),但其物理存储形式却是连续的,即存完第一行,在其后面接着存储第二行,第三行,…,如无特殊说明,本书中涉及对二维数组的表述一般指的是其逻辑形式即矩阵形式。

  二维教组的应用举例

  【例 3】一个班级有 N 名学生,每个学生有 4 门课程(语文、数学、外语、C 语言),计算每个学生的平均分。编写程序实现该功能需求。

  问题分析:该问题可把每个学生当成一个对象,而每个对象(学生)有 5 个属性:4 门课程成绩及平均分。故该问题可使用二维数组来处理数据,该二维数组含有 N 个对象,故第一维的大小为 N,每个对象有 5 个属性,故第二维的大小为 5。为便于验证,本例中学生个数 N 设为 3 个。

  实现代码:

  #include#define N 3int main (void){ float a[N][5],sum; //sum用来累加每个学生4门课的总成绩 int i, j ; printf ("输入%d个学生信息(语、数、外、C语言成绩):\n",N); for(i=0;i

  运行结果为:

  输入3个学生信息(语、数、外、C语言成绩):

  NO1:82 91 88 93

  NO2:83 84 80 91

  NO3:73 79 86 81

  学号 语文 数学 外语 C语言 平均成绩

  NO1: 82.0 91.0 88.0 93.0 88.5

  NO2: 83.0 84.0 80.0 91.0 84.5

  NO3: 73.0 79.0 86.0 81.0 79.8

抱歉!评论已关闭.