当前位置:嗨网首页>书籍在线阅读

22-函数和多维数组

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

10.7.3 函数和多维数组

如果要编写处理二维数组的函数,首先要能正确地理解指针才能写出声明函数的形参。在函数体中,通常使用数组表示法进行相关操作。

下面,我们编写一个处理二维数组的函数。一种方法是,利用 for 循环把处理一维数组的函数应用到二维数组的每一行。如下所示:

int junk[3][4] = { {2,4,5,8}, {3,5,6,9}, {12,10,8,6} };
int i, j;
int total = 0;
for (i = 0; i < 3 ; i++)
     total += sum(junk[i], 4); // junk[i]是一维数组

记住,如果 junk 是二维数组, junk[i] 就是一维数组,可将其视为二维数组的一行。这里, sum() 函数计算二维数组的每行的总和,然后 for 循环再把每行的总和加起来。

然而,这种方法无法记录行和列的信息。用这种方法计算总和,行和列的信息并不重要。但如果每行代表一年,每列代表一个月,就还需要一个函数计算某列的总和。该函数要知道行和列的信息,可以通过声明正确类型的形参变量来完成,以便函数能正确地传递数组。在这种情况下,数组 junk 是一个内含3个数组元素的数组,每个元素是内含4个 int 类型值的数组(即 junk 是一个3行4列的二维数组)。通过前面的讨论可知,这表明 junk 是一个指向数组(内含 4int 类型值)的指针。可以这样声明函数的形参:

void somefunction( int (* pt)[4] );

另外,如果当且仅当 pt 是一个函数的形式参数时,可以这样声明:

void somefunction( int pt[][4] );

注意,第1个方括号是空的。空的方括号表明 pt 是一个指针。这样的变量稍后能以同样的方式用作 junk 。下面的程序示例中就是这样做的,如程序清单10.17所示。注意该程序清单演示了3种等价的原型语法。

程序清单10.17  array2d.c 程序

// array2d.c -- 处理二维数组的函数
#include <stdio.h>
#define ROWS 3
#define COLS 4
void sum_rows(int ar[][COLS], int rows);
void sum_cols(int [][COLS], int);        // 省略形参名,没问题
int sum2d(int(*ar)[COLS], int rows);     // 另一种语法
int main(void)
{
     int junk[ROWS][COLS] = {
               { 2, 4, 6, 8 },
               { 3, 5, 7, 9 },
               { 12, 10, 8, 6 }
     };
     sum_rows(junk, ROWS);
     sum_cols(junk, ROWS);
     printf("Sum of all elements = %d\n", sum2d(junk, ROWS));
     return 0;
}
void sum_rows(int ar[][COLS], int rows)
{
     int r;
     int c;
     int tot;
     for (r = 0; r < rows; r++)
     {
          tot = 0;
          for (c = 0; c < COLS; c++)
               tot += ar[r][c];
          printf("row %d: sum = %d\n", r, tot);
     }
}
void sum_cols(int ar[][COLS], int rows)
{
     int r;
     int c;
     int tot;
     for (c = 0; c < COLS; c++)
     {
          tot = 0;
          for (r = 0; r < rows; r++)
               tot += ar[r][c];
          printf("col %d: sum = %d\n", c, tot);
     }
}
int sum2d(int ar[][COLS], int rows)
{
     int r;
     int c;
     int tot = 0;
     for (r = 0; r < rows; r++)
          for (c = 0; c < COLS; c++)
               tot += ar[r][c];
     return tot;
}

该程序的输出如下:

row 0: sum = 20
row 1: sum = 24
row 2: sum = 36
col 0: sum = 17
col 1: sum = 19
col 2: sum = 21
col 3: sum = 23
Sum of all elements = 80

程序清单10.17中的程序把数组名 junk (即,指向数组首元素的指针,首元素是子数组)和符号常量 ROWS (代表行数 3 )作为参数传递给函数。每个函数都把 ar 视为内含数组元素(每个元素是内含 4int 类型值的数组)的数组。列数内置在函数体中,但是行数靠函数传递得到。如果传入函数的行数是 12 ,那么函数要处理的是 12 × 4 的数组。因为 rows 是元素的个数,然而,因为每个元素都是数组,或者视为一行, rows 也可以看成是行数。

注意, armain() 中的 junk 都使用数组表示法。因为 arjunk 的类型相同,它们都是指向内含 4int 类型值的数组的指针。

注意,下面的声明不正确:

int sum2(int ar[][], int rows);    // 错误的声明

前面介绍过,编译器会把数组表示法转换成指针表示法。例如,编译器会把 ar[1] 转换成 ar+1 。编译器对 ar+1 求值,要知道 ar 所指向的对象大小。下面的声明:

int sum2(int ar[][4], int rows);    // 有效声明

表示 ar 指向一个内含4个 int 类型值的数组(在我们的系统中, ar 指向的对象占16字节),所以 ar+1 的意思是“该地址加上16字节”。如果第2对方括号是空的,编译器就不知道该怎样处理。

也可以在第1对方括号中写上大小,如下所示,但是编译器会忽略该值:

int sum2(int ar[3][4], int rows); // 有效声明,但是3将被忽略

与使用 typedef (第5章和第14章中讨论)相比,这种形式方便得多:

typedef int arr4[4];                // arr4是一个内含 4 个int的数组
typedef arr4 arr3x4[3];             // arr3x4 是一个内含3个 arr4的数组
int sum2(arr3x4 ar, int rows);      // 与下面的声明相同
int sum2(int ar[3][4], int rows);   // 与下面的声明相同
int sum2(int ar[][4], int rows);    // 标准形式

一般而言,声明一个指向N维数组的指针时,只能省略最左边方括号中的值:

int sum4d(int ar[][12][20][30], int rows);

因为第1对方括号只用于表明这是一个指针,而其他的方括号则用于描述指针所指向数据对象的类型。下面的声明与该声明等价:

int sum4d(int (*ar)[12][20][30], int rows); // ar是一个指针

这里, ar 指向一个 12 × 20 × 30int 数组。