天天看点

指针与数组

先看一个例子:

首先指针pa指向a[0]的地址,注意后缀运算符的优先级高于单目运算符,所以是取a[0]的地址,而不是取a的地址。然后pa++让pa指向下一个元素(也就是a[1]),由于pa是int *指针,一个int型元素占4个字节,所以pa++使pa所指向的地址加4,注意不是加1。

从前面的例子我们发现,地址的具体数值其实无关紧要,关键是要说明地址之间的关系(a[1]位于a[0]之后4个字节处)以及指针与变量之间的关系(指针保存的是变量的地址),现在我们换一种画法,省略地址的具体数值,用方框表示存储空间,用箭头表示指针和变量之间的关系。

指针与数组

既然指针可以用++运算符,当然也可以用+、-运算符,pa+2这个表达式也是有意义的,如上图所示,pa指向a[1],那么pa+2指向a[3]

事实上,e1[e2] 这种写法和

(*((e1)+(e2))) 是等价的,*(pa+2) 也可以写成pa[2],pa

就像数组名一样,其实数组名也没有什么特殊的,a[2]之所以能取数组的第2个元素,是因为它等价于 *(a+2),关于数组的基本操作,当数组名做右值时自动转换成指向首元素的指针,所以a[2]和pa[2]本质上是一样的,都是通过指针间接寻址访问元素。

由于(*((e1)+(e2)))显然可以写成(*((e2)+(e1))),所以e1[e2]

也可以写成 e2[e1],这意味着2[a]、2[pa]这种写法也是对的,但一般不这么写。另外,由于a做右值使用时和&a[0]是一个意思,所以int *pa = &a[0] ;通常不这么写,而是写成更简洁的形式int*pa

=a; 。

c语言允许数组下标是负数,现在你该明白为什么这样规定了。在上面的例子中,表达式pa[-1]是合法的,它和a[0]表示同一个元素。

两个指针变量做比较运算(>、>=、<、<=、==、!=)表示什么意义?两个指针变量做减法运算又表示什么意义?

根据以往使用比较运算的经验,就应该猜到pa + 2 > pa,pa - 1 == a,所以指针之间的比较运算比的是地址,c语言正是这样规定的,不过c语言的规定更为严谨,只有指向同一个数组中元素的指针之间相互比较才有意义,否则没有意义。

那么两个指针相减表示什么?pa - a等于几?因为pa - 1 == a,所以pa - a显然应该等于1,指针相减表示两个指针之间相差的元素个数,同样只有指向同一个数组中元素的指针之间相减才有意义。两个指针相加表示什么?想不出来它能有什么意义,因此c语言也规定两个指针不能相加。

在取数组元素时用数组名和用指针的语法一样,但如果把数组名做左值使用,和指针就有区别了。例如pa++是合法的,但a++就不合法,pa

= a + 1是合法的,但a = pa + 1就不合法。数组名做右值时转换成指向首元素的指针,但做左值仍然表示整个数组的存储空间,而不是首元素的存储空间,数组名做左值还有一点特殊之处,不支持++、赋值这些运算符,但支持取地址运算符&,所以&a是合法的。

如下,列举几种等价的函数原型:[如果参数是数组,则等价于参数是指针的形式]

1. void func(int a[10]) { ... }

2. void func(int *a) { ... }

3.  void func(int a[]) { .. }

参数写成指针形式还是数组形式对编译器来说没区别, 都表示这个参数是指针, 之所以规定两种形式是为了给读代码的人提供有用的信息,如果这个参数指向一个元素,通常写成指针的形式,如果这个参数指向一串元素中的首元素,则经常写成数组的形式. 

继续阅读