[关闭]
@phper 2015-07-14T06:04:49.000000Z 字数 2189 阅读 4119

c语言中的指针与数组

c

前言

前面一节,仔仔细细的学习了语言中的指针是干嘛,它的定义、初始化赋值、取值等操纵,有了一个大致的了解。这一节,看指针是如何和数组紧紧结合在一起的。

指针指向数组

前面已经用指针指向一个字符,由于,指针就是指向一个元素的首地址,所以,数组也是一样:

  1. //定义一个有2个整形数字的数组。总共占用4个字节的空间。
  2. int a[2] = {1,4};
  3. int *p; //定义一个整型指针
  4. //将数组的首地址(第一个元素的地址)赋值给指针p,也就是p指向数组a
  5. p = &a[0];
  6. //将指向的首地址的空间值改成5,也就是讲数组的第一个元素改成10
  7. *p = 10;

用图来表示内存的变化就是:

学过数组这章,我们知道。数组名a其实是一个常量,它表示它的第一个元素的地址。也就是说 a == &a[0]。所以,上面的第9行也可以改成:

p = a;

那如果,我想改变a[1]的值咋办呢?那么必须要指针指到a[1]这个位置。那么这个时候,我们可以就要用到指针+1操作来指针移动了。

一开始,p指向a[0], 那么p+1就会指向a[1]了。

需要注意的是,这个+1 并不是内存上的一个字节上的地址加1,而是根据数组内的元素的类型,往下移动一个长度。

所以,上面的+1,实际上是移动了2个字节的位置。那,如果你定义char类型的字符串数组,+1 就会移动一个字节的位置,下面是常用的类型数组内,+1移动的字节数:

所以,你这么理解: +1就表示指地址移动数组下一个元素的位置上了。就可以了。

回到这个例子上,我想取到a[1]的值,就可以这样操作了:

int value = *(p+1); //指针加1,再取出它指向的值。

上面的操作只是取出了指针p的值得到地址,然后根据地址+1,跳到下一个元素的地址上,取到它的值。p始终还是指向&a[0]没变的。

用图表示是:

其实,我们也可以直接移动指针,也可以达到同样的效果, 先移动指针,p的值就变成了ffc5,再根据ffc5取出它的值。

p++; // p = p+1;
int value = *p;

用图表示就是:

指针与数组的关系

数组与指针有着千丝万缕的关系,前面简单的说了一些。

就拿最简单的一个来说。一个数组的名字其实是个常量,它表示的就是这个数组第一个元素的地址。这一点就说明了数组与指针的关系。

我们继续深挖。

用数组名作为函数实参时,是把实参数组的首地址传递给形参数组,两个数组共同占用同一段内存空间,这样形参数组中的元素值发生变化就会使实参数组的元素值也同时变化:

  1. //形参是数组
  2. void change(int b[]) {
  3. b[0] = 10;
  4. }
  5. int main()
  6. {
  7. // 定义一个int类型的数组
  8. int a[4] = {1, 2, 3, 4};
  9. // 将数组名a传入change函数中
  10. change(a); // 实际上传递的就是地址。
  11. // 查看a[0]
  12. printf("a[0]=%d", a[0]); //输出10。
  13. return 0;
  14. }

change函数的形参是数组类型的,在第11行调用change函数时,将数组名a,也就是数组的地址传给了数组b。因此数组a和b占用着同一块内存空间。

这种地址的传递也可以用指针来实现。函数的实参和形参都可以分别使用数组或指针。这样就有4种情况:

实参 形参
数组名 数组
数组名 指针
指针 数组
指针 指针

所以,我们再换其他的几种看看,是不是相同的效果结果

  1. //形参是指针
  2. void change(int *b) {
  3. b[0] = 10;
  4. }
  5. int main()
  6. {
  7. // 定义一个int类型的数组
  8. int a[4] = {1, 2, 3, 4};
  9. // 将数组名a传入change函数中
  10. change(a); // 实际上传递的就是地址。
  11. // 查看a[0]
  12. printf("a[0]=%d", a[0]); //输出10。
  13. return 0;
  14. }
  1. //形参是数组
  2. void change(int b[]) {
  3. b[0] = 10;
  4. }
  5. int main()
  6. {
  7. // 定义一个int类型的数组
  8. int a[4] = {1, 2, 3, 4};
  9. int *p = a //或者 int *p = &a[0];
  10. //将指针p传入change函数中
  11. change(p);
  12. // 查看a[0]
  13. printf("a[0]=%d", a[0]); //输出10。
  14. return 0;
  15. }
  1. //形参是指针
  2. void change(int *b) {
  3. b[0] = 10;
  4. }
  5. int main()
  6. {
  7. // 定义一个int类型的数组
  8. int a[4] = {1, 2, 3, 4};
  9. int *p = a //或者 int *p = &a[0];
  10. //将指针p传入change函数中
  11. change(p);
  12. // 查看a[0]
  13. printf("a[0]=%d", a[0]); //输出10。
  14. return 0;
  15. }

以上四种都是可以改变数组的值的,说明,他们公用的是同一个地址。在很多情况下,指针和数组是可以相互切换使用的。但是,并不能说指针就等于数组。

总结下

p是一个指针,a 是一个数组,p 指向 a 。

那么:
1. p+1表示指向数组该元素的下一个元素。p = &a[0],则p+1表示a[1]的地址。
2. p+i和a+i都可以表示元素a[i]的地址,它们都指向数组的第i个元素。a代表数组首地址,a+i也是地址,它的计算方法与p+i相同
*(p+i)和*(a+i)都表示数组元素a[i]。
3. 虽然p+i和a+i都指向数组的第i个元素,但二者使用时还是有区别的。因为作为指针变量的p可以改变自身值,如p++,使p的值自增。而数组名a是一个代表数组首地址的常量,它的值是不能改变的,即a++是不合法的。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注