原创 C指针温习(二):一维数组与指针

2007-12-26 10:02 5935 12 12 分类: MCU/ 嵌入式

我们知道,每个数组元素都在内存中占有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素,也就是把一个元素的地址放在一个指针变量中。数组元素的指针就是数组元素的地址。


引用一个数组元素可以用下标法,也可以用指针法。用指针法的优点是占用的内存少,运行速度快。


举一个例子:如果我们定义了:


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+ia+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指向数组ap的值就是&a[0]。然后再将p的值传给形参指针变量xx的初始值也是&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条评论)

登录后参与讨论
我要评论
0
12
关闭 站长推荐上一条 /2 下一条