对于上一次博客的补充:
zippo[4][2]
zippo
等价于&z[0]
二维数组首元素的地址(每个元素都是内含两个int类型元素的一维数组)
zippo+2
等价于&z[2]
二维数组的第3个元素(即一维数组)的地址
*(zippo+2)
等价于&z[2][0]
二维数组的第3个元素(即一维数组)的首元素(1个int类型的值)地址
*(zippo+2)+1
等价于&z[2][1]
二维数组的第3个元素(即一维数组)的第2个元素(也是一个int类型的值)地址
*(*(zippo+2)+1)
等价于zippo[2][1]
二维数组的第3个一维数组元素的第2个int类型元素的值
开始今天的博客:
1.指向多维数组的指针
声明一个指针变量pz指向一个二维数组
zippo[n][m]
int (*pz)[m];
但是
int *pax[n];
pax是一个内含n个指针元素的数组,每个元素都指向int的指针
2.指针的兼容性
指针之间的赋值比数值类型之间的赋值更严格。例如,不用类型转换就可以把int 类型的值赋给double类型的变量,但是两个int类型的指针不能这样做。
int n=5;
double x;
int * p1=&n;
double * pd=&x;
x=n; //强制转换
pd=p1; //编译时错误
更复杂的类型也是如此。
假设有如下声明:(指针替换成地址比较好理解)
int * pt;
int (*pa)[3];
int ar1[2][3];
int ar2[3][2];
int **p2; //指向一个指针的指针
有如下的语句:
pt=&ar1[0][0]; //都是指向int的指针
pt=ar1[0]; //都是指向int的指针
pt=ar1; //无效
pa=ar1; //都是指向内含3个int类型元素数组的指针
pa=ar2; //无效
p2=&pt; //都是指向int *的指针
*p2=ar2[0]; //都是指向int的指针
p2=ar2; //无效
对于多重解引用
例如,考虑下面的代码:
int x=20;
const int y=23;
int * p1=&x;
const * p2=&y;
const ** pp2;
p1=p2; //不安全--把const指针赋给非const指针
p2=p1; //有效--把非const指针赋给const指针
pp2=&p1; //不安全--嵌套指针类型赋值
把const指针赋给非const指针不安全,因为这样可以使用新的指针改变const指针指向的数据。(编译器可能给出警告)关于这一部分详情请看11.16日的博客
3.函数和多维数组
3种等价语法(声明形参):
#define ROWS 3
#define COLS 4
void a(int ar [][COLS],int rows);
//第一个[]表明ar是一个指针
void b(int [][COLS],int); //省略形参名,没问题
int c(int (*ar)[COLS],int rows);//另一种语法
也可以在第一对方括号协商大小,但是编译器会忽略该值。
一般而言,声明一个指向N维数组的指针时,只能省略最左边方括号的值:
int sum4d(int ar[][12][20][30],int rows)
等价于
int sum4d(int (*ar)[12][20][30],int rows)
这里ar指向一个12×20×30的int数组
4.变长数组
变长数组有一些限制:
变长数组必须是自动存储类别,这意味着无论在函数中声明还是作为函数形参声明,都不能使用static或extern存储类别说明符。而且不能在声明在初始化他们。
这里的“变”指的是:在创建数组时,可以使用变量指定数组的维度。
首先,要声明一个带二维变长数组参数的函数,如下所示:
int sum2d(int rows,int cols,int ar[rows][cols]); //ar是一个变长数组(VLA)
在形参列表中申明ar之前必须先声明两个形参,因为ar需要这两个形参作为维度。
也可以这样写:
int sum2d(int,int,int ar[*][*]); //ar是一个变长数组(VLA),省略了维度形参名。
需要注意的是,在函数定义的形参列表中声明的变长数组并未实际创建数组。和传统的语法类似,变长数组名实际上是一个指针。这说明带变长数组形参的函数实际上是在原始数组在处理数组,因此可以修改传入的数组。
变长数组还允许动态内存分配(以后再说)
5.复合字面量(简单概括)
int diva[2]={10,20};//正常
(int [2]){10,20};//复合字面量(匿名数组)
因为复合字面量是匿名的,所以不能先创建再使用它,必须在创建的同时使用它。使用指针记录地址就是一种用法。
int * pt1;
pt1=(int [2]){10,20};
还可以把复合字面量作为实际参数传递给带有匹配形式参数的函数:
int sum(const int ar[],int n);
...
int total3;
total3=sum((int []){4,4,4,5,5,5},6)
这种用法的好处是,把信息传入函数前不必先创建数组,这是复合字面量的典型用法。