(1)下标法,如a[i]的形式;
(2)指针法,如*(a+i)
或*(p+i)
。其中a是数组名,p是指向数组元素的指针变量,其初值p=a。
【例】有一个整数数组a,有0个元素,要求输出数组中的全部元素
【分析】:引用数组中各元素的值有3中方法:(1)下标法,如a[3];(2)通过数组名计算元素地址,找出元素的值;(3)用指针变量指向数组元素
(1)下标法:
int main()
{
int a[10];
printf("输入10个数字:");
for (int i = 0; i<10;i++)
scanf("%d",&a[i]);
for (int i = 0;i<10;i++)
printf("%d ", a[i]);//数组元素用数组名和下标表示
return 0;
}
(2)通过数组名计算元素地址,找出元素的值:
int main()
{
int a[10];
printf("输入10个数字:");
for (int i = 0; i < 10; i++)
scanf("%d", &a[i]);
//scanf("%d", a+i);
for (int i = 0; i < 10; i++)
printf("%d ", *(a + i));//通过数组名和元素序号计算元素地址,在找到该元素
return 0;
}
(3)用指针变量指向数组元素:
int main()
{
int a[10];
int* p = &a[0];
printf("输入10个数字:");
for (int i = 0; i < 10; i++)
scanf("%d", &a[i]);
/*for (int p = a; p < (a+10); p++)
scanf("%d", p); */ //用指针变量表示当前元素的地址
for (p=a; p< (a+10);p++)
printf("%d ", *p);//用指针指向当前的数组元素
return 0;
}
3种方法的比较:
第(1)和第(2)种方法执行效率相同。C编译系统是将a[i]转换成*(a+i)处理的,即先计算元素的地址。因此用(1)、(2)两种方法找数组元素费时较多。
第(3)种方法比第(1)和第(2)种方法快,用指针变量直接指向该元素,不必每次都重新计算地址,像p++这样的自加操作是比较快的。
用下标法比较直观,能直接知道是第几个元素。用地址法或指针变量的方法不直观,难以很快判断出当前处理的是哪一个元素。
(1)可以通过改变指针变量的值指向不同的元素。例如,当用指针变量p指向数组中的某一元素,用p++使p的值不断改变从而指向不同的元素。
如果不用p变化的方法而用数组名a变化的方法(例如,用a++)是不行的。因为数组名a代表数组首元素的地址,它是一个指针型常量,所以a++是无法实现的。
(2)要注意指针变量的当前值。具体看下面的例子。
【例】通过指针变量输出整型数组a的10个元素。
【解题思路】用指针变量p指向数组元素,通过改变指针变量的值,使p先后指向a[0]~a[9]各元素。
【程序代码】
int main()
{
int a[10];
int* p = a;
printf("输入10个数字:");
for (int i = 0; i < 10; i++)
scanf("%d", p++);
for (int i = 0; i < 10; i++, p++)
printf("%d ", *p);
return 0;
}
【运行结果】
【结果分析】
(1)输出的数值并不是a数组中各元素的值。
(2)造成这个结果的原因是指针变量p的指向有问题。
指针变量p的初始值为a数组首元素的地址,经过第一个for循环读入数据之后,p已指向该数组的末尾。因此在执行第二个for循环时,p的起始值就不是&a[0]了,而是a+10。由于执行第二个for循环时,每次都要执行p++,因此p指向的是a数组下面的10个存储单元,而这些存储单元的值是不可预测的。如下图所示:
【改正以上代码】
解决办法:在第二个for循环之前加上一个赋值语句:p=a;
,使p的初始值重新等于&a[0],这样结果就对了,代码如下:
int main()
{
int a[10];
int* p = a;
printf("输入10个数字:");
for (int i = 0; i < 10; i++)
scanf("%d", p++);
p = a;
for (int i = 0; i < 10; i++, p++)
printf("%d ", *p);
return 0;
}
1.从上例可以看出,虽然定义数组时指定它包含10个元素,并用指针变量p指向某一数组元素,但是实际上指针变量p可以指向数组以后的存储单元。 如果在上例的程序中我们引用数组元素a[10],虽然并不存在这个元素,但是C编译程序并不认为它非法。系统把它按*(a+10)
处理,即先找出(a+10)的值(是一个地址),然后找出它指向的单元(*(a+10))的内容。这样做虽然在编译时不出错,但是运行结果却不是预期的,应该避免出现这样的情况。在使用指针变量指向数组元素时,应该切实保证指向数组中的有效元素。
2.指向数组元素的指针变量也可以带下标,如p[i]。
带下标的指针变量的含义:当指针变量指向数组元素时,指针变量可以带下标。因为在程序编译时,对下标的处理方法是转换为地址的,对p[i]处理成*(p+i)
,如果p指向一个整型数组元素a[0],则p[i]代表a[i]。但是必须弄清楚p的当前值是什么,如果p当前指向a[3],则p[2]并不代表a[2],而是代表a[3+2],即a[5]。
3.利用指针引用数组元素,比较方便灵活,有不少技巧。主要分析下面几种情况(设p开始指向数组a的首元素(即p=a))。
(1)
p++;
*p;
p++使p指向下一元素a[1]。然后若再执行*p,则得到下一个元素a[1]的值。
(2)*p++;
由于++和*
同优先级,结合方向为自右向左,因此*p++;
等价于*(p++);
。先引用p的值,实现*p
的运算,然后再使p自增1。
上例程序中的第二个for语句
for (int i=0;i<10;i++,p++)
printf("%d ",*p);
可以改写为
for(int i=0;i<10;i++)
printf("%d ",*p++);
作用完全一样。它们的作用都是先输出*p的值,然后使p值加1,这样下一次循环时,*p就是下一个元素的值。
(3)*(p++)
与*(++p)
作用不相同。
*(p++)
是先取*p
的值,然后使p+1。
*(++p)
是先使p+1,再取*p。
若p初值为a(即&a[0]),若输出*(p++)
,得到a[0]的值,而输出*(++p)
,得到a[1]的值。
(4)++(*p)
表示p所指向的元素值加1,如果p=a,则++(*p)
相当于++a[0]
,若a[0]的值为3,则执行++(*p)
(即++a[0]
)之后,a[0]的值为4。注意:是元素a[0]的值加1,而不是指针p的值加1。
(5)如果p当前指向a数组中第i个元素a[i],则:
①*(p--)
相当于a[i--]
,先对p进行*
运算(求p所指向的元素的值),再使p自减。
②*(++p)
相当于a[++i]
,先使p自加,再进行*
运算。
③*(--p)
相当于a[--i]
,先使p自减,再进行*
运算。
【注意】
在用*p++
形式的运算时,一定要十分小心,弄清楚先对p进行*
运算还是先使p自增或自减。