我们知道,每个数组元素都在内存中占有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素,也就是把一个元素的地址放在一个指针变量中。数组元素的指针就是数组元素的地址。
引用一个数组元素可以用下标法,也可以用指针法。用指针法的优点是占用的内存少,运行速度快。
举一个例子:如果我们定义了:
int a[100]; (定义a为包含100个整型数据的数组)
int * p; (定义p为指向整型变量的指针变量)
对指针变是赋值的方法是: p = &a[0]; 它表示把a[0]元素的地址赋给指针变量p。
C语言规定数组名代表数组中的第一个元素(序号为0的元素)的地址。所以下面两句是等价的:
p = &a[0];
p = a;
注意数组名并不代表整个数组,上面第二句是把a数组的首元素的地址赋给指针变量p,而不是把数组a各元素的值赋给p。
在定义指针变量的时候可以对它赋初值:
int * p = &a[0]; //注意不是int p = &a[0];
它等价于:
int * p;
p = &a[0];
C规定:如果指针变量p已经指向数组中的一个元素,那么p+1指向同一数组中的下一个元素,而不是将p的值简单地加1。比如:如果数组中每个元素占两个字节的地址,那么
p+1就是将p的值(是一个地址)加上二个字节,以使它指向下一个元素。
如果p的初值是&a[0],那么:
1、 p+i和a+i就是a的地址,它的实际地址是:a+i*d。
2、 *(p+i)或者*(a+i)是p+i 或者a+i所指向的数组元素,即:a。实际上,在编译时,对数组元素a就是按照*(a+i)处理的。即按数组首元素的地址加上相对位移量得到要找的元素的地址,然后找到该单元的内容。比如:a[3]的地址为: 1000+3*2=1006,由此可以看出,[ ]实际上是变址运算符。
3、 指向数组的指针变量也可以带下标。如:p与*(p+i)是等价的。
使用指针变量的时候,有几个问题要注意:
1、 可以改变指针变量的值。如:
main()
{
int a[10];
int * p,i;
for( i = 0;i<10;i++ )
scanf("%d",&a);
printf("\n");
for( p="a";p<( a+10 );p++ )
printf("%d",* p);
}
这里用p++来使p的值不断改变,这是合法的。那么如果我们将上面倒数二句改成
for( p="a";a<( p+10 );a++ )
printf("%d",* a);
行吗?肯定不行。因为a是一个指针常量,它是固定不变的,a++是什么意思呢?是无法实现的。
2、 要注意指针变量的当前值。
如:
main()
{
int * p,i,a[10];
p = a;
for( i = 0;i<10;i++)
scanf("%d",p++);
printf("\n");
for( i = 0;i<10;i++,p++)
printf("%d",* p);
}
它实际上是有问题的,输出是不可预料的。原因是什么呢?我们来分析一下,指针变量的初始值为a数组首元素(a[0])的地址,但是经过了第一个for循环,p已经指向了a数组的末尾了。所以在第二个for循环的时候,每次执行p++的时候,p指向的是a数组下面的十个元素了,而不是指向a了。而这些存储单元的值是不可预料的。解决的办法有一个,就是在第二个for循环之前加一个赋值语句:
P = a;
这样就对了。
3、虽然定义数组的时候p是指向当前元素的,但是p还可以指向数组以后的内存单元这个时候编译器并不认为非法。但是结果是不可以预料的。应该避免这种情况。
4、注意一些指针运算:
p++是指使p指向下一个元素(是地址)。
* p++,由于++和*是相同优先级,结合方向为自右向左,所以它等价于*( p++),作用是先得到p所指向的变量的值,再使p+1=>p。
(* p)++表示p所指向的元素加1,即(a[0])++。如果a[0]是2,则(a[0])++为3。注意是元素的值加1,而不是指针的值加1。
*(p++)相当于a[i++],先对p进行*运算,再使 p自减。
*(++p)相当于a[++i],先对p自加,再作*运算。
可见,归纳起来,如果有一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参的对应关系有四种情况:
(1) 形参与实参都是数组名,形式如下:
void main()
{
int a[10];
f( a,10 );
}
void f( int x[],int n )
{
}
由于实参数组名接收了实参数组首元素的地址,所以可以认为在函数调用期间,形参数组与实参数组调用共一段内存单元。
(2) 实参用数组名,形参用指针变量。形式如下:
void main()
{
int a[10];
f(a,10);
}
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
void f( int * x,int n )
{
}
这里实参a为数组名,形参x为指向整形变量的指针变量,函数开始执行的时候,x指向a[0],即x = &a[0]。通过x的改变,可以指向a数组的作任一元素。
(3) 实参形参都用指针变量。比如:
void main()
{
int a[10],* p = a;
f(p,10);
}
void f( int x[],int n )
{
}
这种情况先使实参指针变量p指向数组a,p的值就是&a[0]。然后再将p的值传给形参指针变量x,x的初始值也是&a[0],通过x值的改变可以使x指向数组a 的任一元素。
(4) 实参为指针变量,形参为数组名。比如:
void main()
{
int a[10],* p = a;
f(p,10);
}
void f(int x[],int n)
{
}
实参p为指针变量,它指向a[0],形参为数组名x,编译的时候系统把x作为指针变量处理,将a[0]的地址传级形参x,使指针变量x指向a[0]。在函数使用过程中,可以使x的值发生变化,而x就是a。这样,主函数可以使用变化了的数组元素值了。
文章评论(0条评论)
登录后参与讨论