语言中指针与数组这两个概念之间的联系是密不可分的,以至于如果不能理解一个概念,就无法彻底理解另一个概念。
C语言中的数组值得注意的地方有以下两点:
1、C语言中只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来。然而,C语言中数组的元素可以是任何类型的对象,当然也可以是另外一个数组。这样,要“仿真”出一个多维数组就不是一件难事。
2、对于一个数组,我们只能够做两件事:确定该数组的大小,以及获得指向该数组下标为0的元素的指针。其他有关数组的操作,哪怕它们看上去是以数组下标进行运算的,实际上都是通过指针进行的。换句话说,任何一个数组下标运算都等同于一个对应的指针运算,因此我们完全可以依据指针行为定义数组下标的行为。
理解C语言中数组运作机制的注意点:
一、理解如何声明一个数组
1、例如:int calendar[12][31];
声明了calendar是一个数组,该数组拥有12个数组类型的元素,其中每个元素都是一个拥有31个整型元素的数组。因此,sizeof(calendar)的值是(31×12)与sizeof(int)的乘积。
如果calendar不是用于sizeof的操作数,而是用于其他的场合,那么calendar总是被转换成一个指向calendar数组起始元素的指针。
2、任何指针都是指向某种类型的变量,如:int* ip;表明ip是一个指向整型变量的指针。如果一个指针指向的是一个数组中的一个元素,那么我们只要给这个指针加1,就能够得到指向该数组中下一个元素的指针。同样地,给这个指针减1,得到的就是指向该数组中前一个元素的指针。
注意,给一个指针加上一个整数,与给该指针的二进制表示加上同样的整数,两者是截然不同。如果ip指向一个整数,那么ip+1指向的是计算机内存中的下一个整数,在大多数现代计算机中,它都不同于ip所指向地址的下一个内存位置。
3、如果两个指针指向的是同一个数组中的元素,把两个指针相减是有意义的。例如:int* q = p + i;那么我们可以通过q - p得到i的值。需要注意的是如果q和p指向的不是同一个数组,即使它们所指向的地址在内存中的位置正好间隔一个数组元素的整数被,所得到的结果仍然是无法保证其正确性。
4、如果我们在应该出现指针的地方,却采用了数组名来替换,那么数组名就被当作指向该数组下标为0的元素的指针。
例如:int* p;
int a[3];
p = a; 就会把数组a中下标为0的元素的地址赋值给p。如果写成p=&a;在ANSI C中是非法的,因为&a是一个指向数组的指针,而p是一个指向整型变量的指针,它们的类型不匹配。
因此,我们可以知道,*a就是数组a中下标为0的元素的引用。例如,*a=88;就是将数组a中下标为0的元素的值设置为88。同样的道理,*(a+1)就是数组下标为1的元素的引用。以次类推,*(a+i)就是数组a中下标为i的元素的引用。这种写法是如此常用,因此它被简记为a[i]。
二、二维数组
二维数组实际上是一数组为元素的一维数组。
例如:int calendar[12][31];
int *p;
int i;
calendar是一个有12个数组类型元素的数组,它的每个数组类型元素是一个有着31个整型元素的数组,所以,calendar[4]是calendar数组的第5个元素,是calendar数组中12个有着31个整型元素的数组之一。因此,calendar[4]的行为也就表现为一个有着31个整型元素的数组的行为。
例如:sizeof(calendar[4])的结果是31与sizeof(int)的乘积。
例如:p = calendar[4];使指针p指向数组calendar[4]中下标为0的元素。
因此:i=calendar[4][7]=*(calendar[4]+7)=*(*(calendar[4])+7);
显然:用带方括号的下标形式很明显地要比完全用指针来表达要简单得多。
注意:p = calendar;是非法的。
因为calendar是一个二维数组,即“数组的数组”,在此处的上下文中使用calendar名称会将其转换为一个指向数组的指针;而p是一个指向整型变量的指针。
但是:int (*ap)[31];声明了*ap是一个拥有31个整型元素的数组,因此,ap就是一个指向这样数组的指针。因而,我们可以这样写:
int calendar[12][31];
int (*monthp)[31];
monthp= calendar;
这样,monthp将指向数组calendar的第一个元素,也就是数组calendar的12个有着31个元素的数组类型元素之一。