数组的本质其实就是把多个相同数据类型的变量捆绑在一起使用而已。例如int a[10],其实质就是把10个整型变量按照地址从低到高的顺序依次分配在一段连续的40个字节的内存空间上。
1、一维数组
一维数组举例,如清单1:
- #include <stdio.h>
-
- int main(void)
- {
- int a[5] = {1, 2, 3, 4, 5};
-
- int *p = a;
-
-
- int (*pa)[5] = &a;
-
- printf("a = %p, p+1 = %p, pa+1 = %p\n", a, p+1, pa+1);
-
- return 0;
- }
第5行定义了一个一维的整型数组a,共有5个元素,并被初始化为整数1、2、3、4和5。注意,这里所说的“整型数组”的“整型”其实指的是数组中单个元素的数据类型,数组本身的数据类型是一种组合型的数据类型,并没有相应的关键字来表示。
回顾一下指针变量的定义,如第7行定义了一个指针变量p,定义中的关键字int指的是指针变量p将只能匹配整型数据的首地址。而数组名a代表的是其第一个
元素a[0]的首地址(数组名代表着数组的首地址的说法欠准确),也就是说把a(等价于&a[0])赋给指针变量p(这时候就可以说p指向了
a[0]),它们的数据类型是相匹配的,因为a[0]是整型变量,而p只匹配整型数据的首地址。
虽然&a与&a[0]的值相同,但它们的数据类型并不相同,a[0]的数据类型为int,而整个数组的数据类型为int [5](是一种组合数据类型,由int和[5]共同决定),所以与&a匹配的指针变量只能是类似于第10行的pa。
也就是说,数组单个元素的数据类型和指针变量将要指向的数据类型是一致的。
例子输出结果:
- a = 0xbfd2d1b4, p+1 = 0xbfd2d1b8, pa+1 = 0xbfd2d1c8
p+1为第二个元素a[1]的首地址,而pa+1的值是整个数组后面的那个地址。
2、多维数组
在C语言当中,没有所谓的多维数组,只有数组的数组。
多维数组举例,如清单2:
- #include <stdio.h>
-
- int main(void)
- {
- int a[2][3][5] = { { {1, 2, 3, 4, 5 },
- {6, 7, 8, 9, 10},
- {11, 12, 13, 14, 15}
- },
- { {16, 17, 18, 19, 20},
- {21, 22, 23, 24, 25},
- {26, 27, 28, 29, 30}
- }
- };
-
- int (*p0)[2][3][5] = &a;
- printf("&a = %p, p0 = %p, p0+1 = %p\n", &a, p0, p0+1);
-
- int (*p1)[3][5] = a;
- printf("p1 = %p, p1+1 = %p, ***(p1+1) = %d\n", p1, p1+1, ***(p1+1));
-
- int (*p2)[5] = a[0];
- printf("p2 = %p, p2+4 = %p, **(p2+4) = %d\n", p2, p2+4, **(p2+4));
-
- int *p3 = a[1][2];
- printf("p3 = %p, p3+2 = %p, *(p3+2) = %d\n", p3, p3+2, *(p3+2));
-
- printf("a[1][0][0] = %d, a[1][1][0] = %d, a[1][2][2] = %d\n",
- a[1][0][0], a[1][1][0], a[1][2][2]);
-
- return 0;
- }
第5行,a是一个三维数组,共有两个元素,每个元素的数据类型为int [3][5]。
a[0]和a[1]分别都是二维数组,每个数组共有三个元素,每个元素的数据类型为int [5]。
a[0][0]、a[0][1]、a[0][2]、a[1][0]、a[1][1]和a[1][2]分别都是一维数组,每个数组共有五个元素,每个元素的数据类型为int。
例子输出结果:
- &a = 0xbfdb1e88, p0 = 0xbfdb1e88, p0+1 = 0xbfdb1f00
- p1 = 0xbfdb1e88, p1+1 = 0xbfdb1ec4, ***(p1+1) = 16
- p2 = 0xbfdb1e88, p2+4 = 0xbfdb1ed8, **(p2+4) = 21
- p3 = 0xbfdb1eec, p3+2 = 0xbfdb1ef4, *(p3+2) = 28
- a[1][0][0] = 16, a[1][1][0] = 21, a[1][2][2] = 28
结合下面的图形好好地体会为什么输出这样的结果:
3、指针数组和数组指针
指针数组,是一个数组,只不过所有元素都是指针变量。
数组指针,是一个指针变量,只不过它将指向的数据类型为某种形式的数组,如清单1中的pa,清单2中的p0、p1和p2。
指针数组的例子,如清单3:
- #include <stdio.h>
-
- int main(void)
- {
- int a[3] = {1, 2, 3};
- int b[3] = {4, 5, 6};
-
- int *p[2] = {a, b};
-
- printf("p[0][1] = %d, *(p[1]+2) = %d\n", p[0][1], *(p[1]+2));
-
- return 0;
- }
例子输出结果:
- p[0][1] = 2, *(p[1]+2) = 6
第8行定义了一个指针数组,共有两个元素,每个元素都是将要指向整型数据的指针变量。
p[0]的值就是数组a的首地址,所以p[0][1]就等价于a[1]。
p[1]的值就是数组b的首地址,然后加2获得数组b第3个元素的首地址,最后通过间接运算符(*)取出第3个元素的值。
4、数组在函数形参中的应用
数组在函数形参中等价于一个相应的指针变量。如清单4:
- #include <stdio.h>
-
- void func(int a[2][3])
- {
- int i, j;
-
- for (i = 0; i < 2; i++)
- for (j = 0; j < 3; j++)
- printf("a[%d][%d] = %d\n", i, j, a[i][j]);
- }
-
- int main(void)
- {
- int b[2][3] = { {1, 2, 3}, {4, 5, 6} };
-
- int (*p)[3] = b;
-
- func(p);
-
- return 0;
- }
例子输出结果:
- a[0][0] = 1
- a[0][1] = 2
- a[0][2] = 3
- a[1][0] = 4
- a[1][1] = 5
- a[1][2] = 6
整个例子的含义是p =&b[0],而数组指针a = p。