C语言中的指针-通过指针引用数组元素-学习笔记-27

引用数组元素

前面我们学习了,引用数组有两种方式:
下标法:a[i]
指针法:(a+1) / (p+1)
今天我们实际来通过这两种方式引用一下数组元素看看能有哪些不同。

例题-1

有一个整形数组 a,有10个元素,要求输出数组中的全部元素。

思路

现在我们会使用3中方法,下标法、数组名计算元素地址、指针变量指向数组元素。

下标法

1
2
3
4
5
6
7
8
#include <stdio.h>
int main(){
int a[10]={1,2,3,4,5,6,7,8,9,10};
for (int i=0;i<10;i++){
printf("%d\n",a[i]);
}
return 0;
}

我们通过遍历数组的下标,输出数组。

数组名地址法

1
2
3
4
5
6
7
8
#include <stdio.h>
int main(){
int a[10]={1,2,3,4,5,6,7,8,9,10};
for (int i=0;i<10;i++){
printf("%d\n",*(a+i));
}
return 0;
}

我们通过改变数组名的地址,输出数组。

指针法

1
2
3
4
5
6
7
8
9
#include <stdio.h>
int main(){
int a[10]={1,2,3,4,5,6,7,8,9,10};
int *p;
for ( p=a;p<(a+10);p++){
printf("%d\n",*p);
}
return 0;
}

我们通过改变指针的地址,来输出数组。

比较三种方法的不同

  1. 第一种和第二种方法,都是通过数组 a 或者 a[i] 来找到内存地址,然后再输出数组元素。而第三种,省去了数组转到地址的这个步骤,直接操作地址,速度更快。
  2. 第三种我们在 for 循环的首行内,直接将地址进行加1操作,速度是最快的,直接改地址,不用从地址到元素,再从元素计算地址之间转换了。
  3. 第二、三种的缺点就是,很难知道当前是哪个元素在执行,因为没有下标,所以很难定位元素。

我们在第三种方式中,将指针进行了 p++ 的操作,如果我们在第二种以数组 a 作为起始地址进行 a++ 操作行不行呢?这样是错误的,因为数组名 a,表示的是该数组的首元素地址,这是一个常量,不能进行改变,所以我们职能以 a 为基础,进行计算。

例题-2

输入10个数到整型数组,通过指针变量输出整型数组 a 的10个元素。

解答

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
int main(){
int a[10];
int *p;
p=a;
for (int i=0;i<10;i++){
scanf("%d",p++);
}
for(int i=0;i<10;i++,p++){
printf("%d",*p);
}
return 0;
}

输出结果是
1 2 3 4 5 6 7 8 9 10
-272632472-272632464-272632456-272632448-272632440-272632432-272632424-272632416-272632408-272632400

我们知道出错了,因为输出的值不对,那么到底值哪里出错了呢?答案就是 p 的地址出错了。
我们在第一次 for 循环执行结束后,p 的地址已经是数组的最末端了,因为要接受数组所有元素的数据,所以 p 的地址已经是 a[9] 的地址了。
下面的输出时,我们又开始累加 p 的地址,自然会出错,所以要解决这个问题,就需要在两个 for 循环中增加一个:

1
p=a;

因为 a 是一个常量,常量的内容是数组 a 的首元素的地址,所以我们需要用 a 这个常量把 p 的地址初始化到数组的首元素地址。可能比较绕,我们可以理解为,再次让 p 回到数组的首元素就好了,这样在第二个 for 循环中,就可以正常的使用 p++ 了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
int main(){
int a[10];
int *p;
p=a;
for (int i=0;i<10;i++){
scanf("%d",p++);
}
p=a; //初始化 p 的值到数组首
for(int i=0;i<10;i++,p++){
printf("%d",*p);
}
return 0;
}

输出结果是
1 2 3 4 5 6 7 8 9 10
12345678910

从上面错误的例子我们可以看出:

  1. 通过改变地址,我们是可以看到非法地址所指向的内容的,比如我们有10个元素,但如果地址移动到第 11 个元素,也不会报错。
  2. 其实指针也可以加一个下标,如 p[5] ,但是需要注意,如果 p 当前指向 a[2] ,然后p[5]实际指向的是,p[2+5]=p[7],所以尽量不要使用指针加下标的形式。

灵活的指针应用

因为指针运行速度更快,所以专业人士一般经常直接操作指针,也有很多操作指针的技巧:

累加移动地址位置

1
2
3
4
int a[5]={1,2,3,4,5};
int *p=a;
p++;
printf("%d",*p);

这是普通的用法, p++ 移向下一个元素,然后 *p 输出结果。

输出后直接累加

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
int main(){
int a[5]={1,2,3,4,5};
int *p;
p=a;
int i=0;
while (i<5) {
printf("%d\n",*p++);
i++;
}
}

这里要特别注意了, 和 ++ 的运算级别相同,所以从左到右依次运算,在上面这个例子中,是先输出p 的值,然后再自加,下次输出时,就可以输出自加后的结果了。

(++p) 和 (p++)

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
int main(){
int a[5]={1,2,3,4,5};
int *p;
p=a;
int i=0;
while (i<4) {
printf("%d\n",*(++p));
i++;
}
}

p++:是先引用 p 的值,再进行累加 ++p:是先累加,再引用 p 的值

所以上面的结果是:

2
3
4
5

++(p) 和 (p)++

我们知道 p 表示的是指针变量 p 所指向的值,所以++(p)同等于 ++a[i],也就是指向的值累加,(*p)++相同,也是 p 所指向的值进行累加。

注意
说了这么多就是想弄清住,*p++,一定是先引用 p 所指向的值,然后再把这个地址累加,千万别弄错先后顺序。

尾巴

这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。


-------------The End-------------
欢迎请我喝咖啡哦~!