C语言的指针与多维数组( 三 )


p2 = p1;// valid-- assigning non-const to const但是进行两级解引用时,这样的赋值也不安全,例如,考虑下面的代码:
const int **pp2;int *p1;const int n = 13;pp2 = &p1; // allowed, but const qualifier disregarded*pp2 = &n; // valid, both const, but sets p1 to point at n*p1 = 10;// valid, but tries to change const n发生了什么?如前所示,标准规定了通过非const指针更改const数据是未定义的 。例如,在Terminal中(OS X对底层UNIX系统的访问)使用gcc编译包含以上代码的小程序,导致n最终的值是13,但是在相同系统下使用clang来编译,n最终的值是10 。两个编译器都给出指针类型不兼容的警告 。当然,可以忽略这些警告,但是最好不要相信该程序运行的结果,这些结果都是未定义的 。
C const和C++ constC和C++中const的用法很相似,但是并不完全相同 。区别之一是,C++允许在声明数组大小时使用const整数,而C却不允许 。区别之二是,C++的指针赋值检查更严格:
const int y;const int * p2 = &y;int * p1;p1 = p2;// error in C++, possible warning in CC++不允许把const指针赋给非const指针 。而C则允许这样做,但是如果通过p1更改y,其行为是未定义的 。
函数和多维数组如果要编写处理二维数组的函数,首先要能正确地理解指针才能写出声明函数的形参 。在函数体中,通常使用数组表示法进行相关操作 。下面,我们编写一个处理二维数组的函数 。一种方法是,利用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] -- one-dimensional array记住,如果junk是二维数组,junk[i]就是一维数组,可将其视为二维数组的一行 。这里,sum()函数计算二维数组的每行的总和,然后for循环再把每行的总和加起来 。
然而,这种方法无法记录行和列的信息 。用这种方法计算总和,行和列的信息并不重要 。但如果每行代表一年,每列代表一个月,就还需要一个函数计算某列的总和 。该函数要知道行和列的信息,可以通过声明正确类型的形参变量来完成,以便函数能正确地传递数组 。在这种情况下,数组junk是一个内含3个数组元素的数组,每个元素是内含4个int类型值的数组(即junk是一个3行4列的二维数组) 。通过前面的讨论可知,这表明junk是一个指向数组(内含4个int类型值)的指针 。可以这样声明函数的形参:
void somefunction( int (* pt)[4] );另外,如果当且仅当pt是一个函数的形式参数时,可以这样声明:
void somefunction( int pt[][4] );注意,第1个方括号是空的 。空的方括号表明pt是一个指针 。这样的变量稍后能以同样的方式用作junk 。下面的程序示例中就是这样做的,如程序array2d.c所示 。注意该程序清单演示了3种等价的原型语法 。
// array2d.c -- functions for 2d arrays#include <stdio.h>#define ROWS 3#define COLS 4void sum_rows(int ar[][COLS], int rows);void sum_cols(int [][COLS], int );// ok to omit namesint sum2d(int (*ar)[COLS], int rows); // another syntaxint 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 = %dn", 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 = %dn", 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 = %dn", 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;}程序array2d.c中的程序把数组名junk(即,指向数组首元素的指针,首元素是子数组)和符号常量ROWS(代表行数3)作为参数传递给函数 。每个函数都把ar视为内含数组元素(每个元素是内含4个int类型值的数组)的数组 。列数内置在函数体中,但是行数靠函数传递得到 。如果传入函数的行数是12,那么函数要处理的是12×4的数组 。因为rows是元素的个数,然而,因为每个元素都是数组,或者视为一行,rows也可以看成是行数 。
注意,ar和main()中的junk都使用数组表示法 。因为ar和junk的类型相同,它们都是指向内含4个int类型值的数组的指针 。
注意,下面的声明不正确:
int sum2(int ar[][], int rows); // faulty declaration


推荐阅读