C/C++数组本质论

C/C++数组本质论
理解数组名
注:为方便讲解,本节所说的指针的类型,指的是指针指向的类型。
1.对数组名的简单理解
数组名表示的是数组的首地址。比如一维数组int a[11]的数组名a就表示数组的首地址。这里的首地址让人产生误解,认为数组名表示的是整个一维数组的地址,其实数组名指向的是数组中第一个元素地址的指针(即a指向的地址是&a[0]),表示整个数组的地址是&a,&a表示的是一个包含有4个元素的一维数组的地址。虽然a和&a的地址值是相同的,但是它们的类型不一样,a的类型是“指向int的指针”,而&a的类型是“指向一个有11个元素的一维数组的指针”。
2.对数组名的理解
关键内容,请认真理解。
(1)对数组名的理解,关键是要明白数组名所表示的指针的类型。
(2)重点规则:数组名是指向第一个元素地址的指针,这个指针的类型是“指向某类型的指针”,这个指针是个常量。比如int a[4]={0};,数组名a是一个指向第一个元素a[0]地址的指针(即a指向的地址是&a[0]),它的类型是“指向int的指针”,这个指针是个常量,即不能改变它的值;再比如int b=1;,则a=&b;是错误的。
(3)对多维数组的数组名的理解:对于多维数组,重点是要理解它的第一个元素是什么。通常讲的多维数组其实就是数组中的数组,即元素为数组的数组。因为数组名是指向数组中第一个元素地址的指针,所以多维数组的数组名指向的也是第一个元素地址的指针,但这个元素又是一个数组或多维数组。因此对于N维数组,数组名是指向第一个元素地址的指针,这个指针的类型是“指向N-1维数组的指针”,指向的数组有N-1维,每维有相对应的X个元素,每个元素都是某类型的(如int)。而一维数组的数组名的类型一般是“指向某类型的指针”。
示例:
 int a[3];,数组名a指向的是第一个元素地址&a[0]的指针,其类型是“指向int的指针”。
 a[2][3];,数组名a指向的是第一个元素a[0]地址的指针(即a指向的地址是&a[0]),这个元素是一个有3个元素的一维数组,因此数组名是一个指向有3个元素的数组的指针,即数组名与指针(*p)[3]的类型相同,它的类型是“指向一维数组的指针”。这个一维数组有3个元素,即数组名代表的地址是第一个元素的地址&a[0],这与一维数组的数组名是相同的。
 b[3][4][5]={0};,数组名b是指向第一个元素b[0]地址的指针,即b指向的地址是&b[0]。这个元素存储的是45的二维数组,因此这个指针的类型是“指向二维数组的指针”,指向的数组有两维,第一维有4个元素,第二维有5个元素。注意:多维数组是数组的数组,三维数组b的第一个元素是b[0],而不是b[0][0][0]。
(4)从以上概念可以看出,多维数组的数组名所表示指针的类型与多维数组的第一维的大小是没有关系的,只与除第一维外的其他维的大小有关系,但这并不意味着第一维的大小就没有作用了。
(5)对数组名取址的运算:若对数组名进行取址运算,则产生的结果是指向整个数组的指针,即对于一维数组名,取址后的类型是“指向一维数组的指针”,这个数组有X个元素;对N维数组名取址后的类型是“指向N维数组的指针”,这个N维数组的每一维有X个元素。比如int a[2][3][4]={0};,则&a;的类型是“指向三维数组的指针”,这个数组的第一维有二个元素,第2维有3个元素,第三维有4个元素。
(6)注意:早期C语言没有“数组的地址”这一概念,所以对数组名取址要么非法,要么就等于数组名本身。
(7)数组名的类型相同,应满足以下条件。
① 对于N维数组,声明时的元素类型要相同。
② 数组名的类型应指向相同维数的数组。
③ 指向的数组的每一维的元素个数必须相同。
④ 示例:int a[2][3][4]; int b[5][3][4]; int c[2][3][5]; float d[2][3][4]
 数组名a和b:所表示的指针具有相同的类型,a和b的元素的类型相同,都是int类型。且a和b的类型都是“指向二维数组的指针”,指向的数组的第一维都有3个元素,第二维都有4个元素。
 数组名a和c:所表示的指针具有不相同的类型,虽然它们都是指向二维数组的指针且元素的类型相同,但数组名a指向的二维数组的第二维有4个元素,而c指向的二维数组的第二维有5个元素,即它们所指向的数组的每一维的元素个数不相同。
 数组名a和d:具有不相同的类型,虽然它们都是指向二维数组的指针,且每一维相对应的元素个数也相等,但a和d的元素类型不相同,a的元素类型是int,而d的元素类型是float。
3.数组名与指针的关系
(1)对于N维数组a,其数组名a与&a[0],&a[0][0],&a[0][0]…[N]的地址值是相同的,也就是说,指向的是相同地址的指针。但要注意,这些指针的类型并不相同,虽然数组名a和&a[0]类型相同,都是“指向N-1维的指针”,&a[0][0]和a[0]类型相同都是“指向N-2维的指针”,但是&a[0]和&a[0][0]类型并不一样,因为对指针进行运算时,指针偏移多少个字节是由其类型决定的,所以在进行运算时它们的偏移量是不一样的。比如有二维数组int a[2][3],假设int类型占据4个字节的内存空间,则&a[0]所指向对象的长度占据12个字节的内存空间(因为它指向的对象是具有3个元素的数组),而&a[0][0]所指向对象的长度只占据4个字节的内存空间,对其进行算术运算时,&a[0]+1将移动12个字节,而&a[0][0]+1则只移动4个字节。对于N维数组也是同样的道理。
(2)对N维数组的数组名做加法运算,将使其指向N维数组中第X个元素的地址。比如对于N维数组a,可以使用&a[0]+1或a+1来表示指向第二个元素的地址&a[1]。
(3)对于N维数组,直接书写成M(0<M<N)维数组,当对其进行指针偏移运算时,并不能像数组名那样直接进行偏移,而应将其替换为指向的实际地址再进行偏移,否则容易出错。比如int a[3][4][5][6]={0};,则a[1]指向的是&a[1][0],当进行a[1]+1偏移运算时,并不是指向a[2],而是指向&a[1][1]。也就是说,a[1]+1并没有指向地址&a[2][0],因为a[1]+1=&a[1][0]+1=&a[1][1](具体请参阅后文中的“指针与数组的混合运算”)。
4.判断指针指向的是几维数组的指针
(1)对于N维数组,若直接书写成M(0<M<N)维数组,则指针是“指向N-M-1维的指针”。指向的数组有N-M-1维,每一维有相对应的X个元素,指向的地址是M维之后第一个元素的地址。比如int a[2][3][4][5][6][7]={0};,则a[1][2][3]的指针指向的是二维数组(6-3-1)的指针,这里a是六维数组,即N=6,书写成三维数组,即M=3。再比如int a[3][4][5][6]={0};,则a[2];是指向&a[2][0]的指针,它的类型是“指向2维数组(4-1-1)的指针”,这个2维数组第一维有5个元素,第二维有6个元素;a[1]指向的是&a[1][0],类型是“指向二维数组的指针”,这个二维数组的第一维有5个元素,第二维有6个元素,可以看到a[2]与a[1]的类型是相同的,但指向的地址值不相同。
(2)对于N维数组,若书写成带有&(取址)运算符的M维数组形式时,则指针指向的是“N-M维的指针”,或者说M维之后还有多少维就是指向多少维的指针。比如int a[2][3][4][5][6][7]={0};,则&a[1][2]指向的是四维数组(6-2)的指针,这里a是六维数组,即N=6,书写时是带取址运算符的2维数组,因此M=2。或者说a是6维数组,而&a[1][2]只写了二维,二维之后还有四维,因此&a[1][2]是指向四维数组的指针。
5.数组名与指针的不同
(1)当数组名作为sizeof的操作数时,返回的是整个数组的长度,而不是指向数组的指针的长度(在32位机器上指针的长度一般为4个字节)。比如int a[5]={0};,则sizeof(a);的结果是20(假设int类型占4个字节),而不是指针的长度4,因为a有5个元素,每个元素占4个字节;再比如int a[3][4]={0};,则sizeof(a);的结果是48,即344=48。
(2)当数组名作为&的操作数时,所产生的将是一个“指向一维或多维数组的指针”,而不会返回指针本身的地址。比如int a[4];,则&a;将产生一个“指向一维数组的指针”,这个一维数组有4个元素。
(3)注意:数组名虽然是一个指针,但它与指针并不完全相同,比如数组名作为sizeof和&的操作数时。
以上内容摘自本人所作《C++语法详解》一书,电子工业出版社出版

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值