对于一维数组,数组名就是存储空间的首地址,即第一个元素的地址,比如下面的程序:
#include <strdio.h>
int main(){
int a[6],i;
int *p = a; /* a就是数组的起始地址,即第一个元素的地势:&a[0] */
for(i = 0 ; i<6;i++)
scanf("%d",&a[i]);
/*等价于
scanf("%d",a+i);
scanf("%d",p+i);
*/
/* *(a+i)等价于a[i] */
for(i = 0 ; i<6;i++){
printf("%d",*p++);
/*等价于
printf("%d",*(a+i));
printf("%d",*(p+i));
*/
}
}
假设定义二维数组a[3][4]如下:
int a[3][4]={ {0,1,2,3},
{4,5,6,7},
{8,9,10,11}
};
那么数组名a是什么呢?是a[0][0]的地址“&a[0][0]”吗?
答案:不是
那么二维数组名到底是一个什么样的指针呢?
C语言的二维数组可以看成数据元素是一维数组的一维数组,因此,a代表整个二维数组的首地址,也是二维数组(下标为0的)第1行的首地址。
即a是a[0]的地址,即&a[0]。而a[0]本身又是一个“含4个元素的一维数组”,因此a[0]是a[0]这个数组的起始地址,即其第一个元素的地址,即a[0]就是&a[0][0]。因此可以用一个int *
指针来访问a[0]:
int *p = a[0];
for(int i = 0 ; i<4;i++)
printf("%d",*p++); /*先输出*p,再p++ */
/*或*/
int *p = a[0];
for(int i = 0 ; i<4;i++)
printf("%d",p[i]);
那么,自然得,会想a应该是一维数组{a[0],a[1],a[3]}的起始地址,既然a[i]的类型是int *
,那么a的类型自然应该是 (int *)*即int **
类型了?
于是,你可能这样写:
int **q = a;
编译器遇到下列语句会报错:
[-Wincompatible-pointer-types]
q = a;
^
Why? (为什么?) 你会大叫!
看一下这张图:
a是第1行(即a[0])的指针,那么a+1就是第2行(即a[1])的指针,a+2就是第3行(即a[2])的指针,…
a确实是一个“一维数组”,但其中的元素是“含4个元素的一维数组”而不是“int *”
类型的元素。
我们知道不管是什么类型的指针int *、int **、double *
,通常占据4个字节,但“含4个元素的一维整型数组”本身就是一种类型啊,它占据的是4*4
个字节(假如一个整数占4个字节的话)。
因此,a是数据元素是“含4个元素的一维整型数组”的一维数组。 那么a作为这个数组的首地址,其类型应该
含4个元素的一维整型数组 *
因此,可以如下写:
含4个元素的一维整型数组 *q = a;
当然这是伪代码,真正的C代码应该写成:
int (*q) [4] = a;
那么为什么不写成:
int[4] *q = a;
这你们要去问C语言的设计者了!
示例:
#define _CRT_SECURE_NO_WARNINGS /* for VS*/
#include <stdio.h>
#include <stdlib.h>
#define M 3
#define N 4
int main(){
int *p;
int(*q)[N];
int a[M][N];
int i, j;
p = *a;
for (i = 0; i<M; i++)
for (j = 0; j<N; j++)
scanf("%d", &a[i][j]);
printf("first output\n");
p = *a;
for (i = 0; i<M; i++){
for (j = 0; j<N; j++)
printf(" %d", *p++);
printf("\n");
}
printf("second output\n");
q = a;
for (i = 0; i<M; i++, q++){
/* q是a[i]的指针*/
p = *q; /* p就是a[i]本身,4个整数构成的一维数组*/
for (j = 0; j<N; j++)
printf(" %d", *p++);
printf("\n");
}
return 0;
}
您的打赏是对我最大的鼓励!