C语言–通过指针引用数组
文章目录
前言
指针不仅可以指向变量,也可以指向数组,数组元素在内存中占用存储单元,因此他们都有相应的地址,用指针指向数组元素就是数组元素的地址。
int a[5] = {1,2,3,4,5};//定义5个整型数据的数组a
int *p;//定义一个整型变量的指针
p=&a[0];//把a[0]的地址赋给指针p
数组名代表数组的首地址,序号为0的元素,也代表数组的首地址
p=&a[0]; //P的值是a[0]的地址
等价于
p=a; //p的值是数组a首元素(即a[0])的地址
数组名仅仅代表数组首元素的地址
对指针变量进行初始化
int *p=&a[0];
等价于
int *p;
p = &a[0];
也可以写成
int *p = a;//把a数组的首地址(a[0])赋给指针变量p而不是*p;
提示:以下是本篇文章正文内容,下面案例可供参考
1.引用数组元素时指针的运算
在指针指向数组元素时,指针变量p指向数组元素a[0],p+1表示指向下一个元素a[1]。这样对引用数组元素提供很大的方便。
可以做如下运算:
- 加一个整数(+或+=),如p+1;
- 减一个整数(-或-=),如p-1;
- 自加运算,如p++,++p;
- 自减运算,如p- -,- -p;
两个指针相减,如p1-p2,只有在p1和p2都指向同一个数组中的元素才有意义
具体说明如下:
(1)如果指针变量P已指向数组中的一个元素,则P+1指向数组中的下一个元素,p-1指向同一数组的上一个元素,p+1并不是将p的地址简单的加1,而是加上一个数组元素所占用的字节数,例如,数组元素是float类型,每个元素占4个字节,p+1意味着使p的地址加4个字节,便于指向下一个元素。p+1所代表的地址实际上是p+pxd,d是一个数组元素所占的字节数。如p的地址是2000,则p+1的值是2000+4=2004,而不是2001。
在定义指针类型时,已经规定了该指针的类型,所以在进行加减时,是根据定义的数据来计算。
(2)当p指向&a[0]时,p+i和a+i是数组元素a[i]的地址,即指向a数组序号为i的元素。计算方法和p+1相同,实际地址为a+1xd。例如,p+9等同于a+9都表示指向a[9]的地址。其值为&a[9]。
(3)*(p+i)
和*(a+i)
是p+i或a+i所指向的数组元素,即a[i]。例如:*(p+5)
或*(a+5)
就是a[5]。即:*(p+5)
等价于*(a+5)
等价于a[5]
。对数组元素a[i]就是按*(a+i)
进行处理,数组首元素地址+相对位移==元素的地址,然后找出该单元中的内容。若数组a的首元素地址为1000,数组类型为float,则a[3]的地址是:1000+3x4=1012,从地址为1012的地方取出a[3]的值。
(4)如果p1和p2都指向同一数组中的元素,如执行p2-p1,结果是p2-p1的值(两个地址之差)除以数组元素的长度。假设,p2指向实型数组元素a[5],p2的职位2020,p1指向a[3],其值为2012,则p2-p1=(2020-2012)/4=2。这表示p2和p1相差2个元素。
两个地址不能相加,无意义
2.通过指针引用数组元素
引用数组的方式
- 直接引用—a[i]
- 间接引用—
*(a+i)
和*(p+i)
。a为数组名,p是执行数组元素的指针变量,其初值为p = a。
示例:用三种方法输出元素中的全部元素。
有一个整型数组a,10个元素,要求输出数组中的全部元素。
方法一:直接引用a[i]
#include<stdio.h>
int main()
{
int a[10];
int i;
printf("please input 10 numbers:");
for(i = 0;i < 10;i++)
scanf("%d",&a[i]);
for(i = 0;i<10;i++)
printf("%d",a[i]);
printf("%\n");
return 0;
}
方法二:间接引用-通过数组名计算数组元素地址,找到元素的值
#include<stdio.h>
int main()
{
int a[10];
int i;
printf("please input 10 numbers:");
for(i = 0;i < 10;i++)
scanf("%d",&a[i]);//可以改为scanf("%d",(a+i));
for(i = 0;i<10;i++)
printf("%d",*(a+i));
printf("%\n");
return 0;
}
&a[i]表示a[i]元素的地址,可以改为(a+i)表示
方法三:间接引用-通过指针变量指向数组元素
#include<stdio.h>
int main()
{
int a[10];
int i;
int *p;
p = &a[0]; //p = a; 指向首地址
printf("please input 10 numbers:");
for(i = 0;i < 10;i++)
scanf("%d",(a+i));
for(p = a;p<(a+10);p++)
printf("%d",*p);
printf("%\n");
return 0;
}
变3:间接引用-通过指针变量指向数组元素
通过指针变量输出整型数组a的10个元素。
思路:用指针变量p指向数组元素,通过改变指针变量的值,使P先后指向a[0]~a[9]各元素。
#include<stdio.h>
int main()
{
int a[10];
int i;
int *p;
p = &a[0]; //p = a;
printf("please input 10 numbers:");
for(i = 0;i < 10;i++)
scanf("%d",p++);
for(i = 0;i<10;i++,p++)
printf("%d",*p);
printf("%\n");
return 0;
}
输出结果:
分析:指针变量p首先指向的是数组a的首地址,第一个for循环中,指针变量已经指向了数组a的末尾,因此在执行第二个for循环时,p已经指向了a数组的末尾,而不是&a[0],而是a+10。
解决办法:
在给指针变量赋值后,需要将p的指向重新指向数组的开头。
#include<stdio.h>
int main()
{
int a[10];
int i;
int *p;
p = &a[0]; //p = a;
printf("please input 10 numbers:");
for(i = 0;i < 10;i++)
scanf("%d",p++);
p = a;
for(i = 0;i<10;i++,p++)
printf("%d",*p);
printf("%\n");
return 0;
}
虽然定义的数组有10个元素,并用指针变量P指向某一数组元素,但实际上指针变量P可以指向数组以后的存储单元。P指向a[10],编译器并不认为是非法的。系统会把它按*(a+10)
处理,即先找到(a+10)的地址,然后找出它指向的单元(*(a+10))
的内容。虽然编译时不出错,但是运行结果是无法预期的,应该避免出现这样的情况。
易混点:*(p++)
和*(++p)
和*p++
假设p开始指向数组a的首元素(即p=a);
1、p++;*p;
分析:p++使p指向下一元素a[1],然后在执行*p,则得到下一个元素a[1]的值
2、*p++;
由于++和*属于同级,结合方向为自右而左,因此等价于*(p++)。先引用p的值,实现*P的运算,然后再使p自增1。
则程序中的第二个for循环可以改写为:
for(i = 0;i<10;i++,p++)
printf("%d",*p);
改写为:
for(i = 0;i<10;i++)
printf("%d",*p++);
作用完全一样,都是先输出*p
的值,然后使p值加1,(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)表示p所指向的元素值加1。
例如:
p = a; //a[0] = 3
则++(*p)相当于++a[0],最后输出的值为4
注意:是元素值加1,不是指针变量加1
5、如果P当前指向a数组中第i个元素a[i]
*(p--)相当于a[i--],先对p进行“*”运算(求p所指向元素的值),在使p自减。
*(p++)相当于a[++i],先使p自加,再进行“*”运算
*(--p)相当于a[--i],先使p自减,在进行“*”运算
例如:想输出a数组的100个元素
p = a;
while( p < a+100)
printf("%d",*p++);
或者
p = a;
while( p < a+100)
printf("%d",*p);
p++;
3.用数组名作为函数参数
实参数组名代表改数组首元素的地址,而形参是用来接收从实参传递过来的数组首元素地址的,因此,形参是一个指针变量,只有指针变量才能存放地址,C编译中都是将形参数组名作为指针变量来处理的。
例如:
fun(int arr[],int n);
等价于
fun(int * arr,int n);
在函数被调用时,系统会在fun函数中建立一个指针变量arr,用来存放从主调函数传递过来的实参数组首元素的地址。如果在fun函数中用运算符sizeof测定arr所占的字节数,可以发现sizeof(arr)的值为4。这就把arr作为指针变量来处理的。
当arr接收了实参数组的首元素地址后,arr就指向实参数组首元素,也就是array[0]。*arr
就是array[0]。arr+1指向array[1]。arr+2也就是array[2],arr+3指向array[3]。即*(arr+1)
等价于array[1],*(arr+2)
等价于array[2],*(arr+3)
等价于array[3]。
*(arr+i)等价于arr[i]
因此在调用函数时,arr[0]和*arr
以及array[0]都表示数组array序号为0的元素。
另外一个角度:
可以认为一个形参数组,它是从实参数组那里得到的起始地址,因此形参数组与实参数组共占同一段内存单元,在调用函数期间,如果改变了形参数组的值,也就改变了实参数组的值,在主调函数中就可以利用这些已经改变的值。
注意:实参数组名代表一个固定的地址,或者说是指针常量,但是形参数组名并不是一个固定的地址,而是按指针变量处理
例:将数组a中的n个整数按相反顺序存放
解题思路:将a[0]与a[n-1]对换,在将a[1]与a[n-2]对换,直到将a[(n-1)/2]与a[n-((n-1)/2)-1]对换。采用循环处理此问题,设置指示变量i和j,i的初值为0,j的初值为n-1。将a[i]与a[j]交换,然后使i的值加1,j的值减1,在将a[i]与a[j]对换,直到i = (n-1)/2为止。
方法1:利用数组作为形参
用一个函数inv来实现交换,实参用数组名a,形参可用数组名,也可以用指针变量。
#include<stdio.h>
void inv(int x[],int n);
int main()
{
int a[10] = {3,7,9,11,0,6,7,5,4,2};
int i;
printf("The original array:\n");
for(i = 0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
inv(a,10);
printf("The array has been inverted:\n");
for(i = 0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
return 0;
}
void inv(int x[],int n)
{
int i,j,temp,m = (n-1)/2;
for(i = 0;i<=m;i++)
{
j = n-1-i;
temp = x[i];
x[i] = x[j];
x[j] = temp;
}
return;
}
运行结果:
在定义inv函数时,可以不指定形参数组x的大小(元素的个数),因为形参数组名实际上是一个指针变量,并不是真正的开辟一个数组空间,定义实参数组是必须指定数组大小,因为需要开辟相应的存储空间。
方法2:利用指针变量作为形参
将inv中的形参x改为指针变量,相应的实参仍可以是数组名a,即数组a首元素的地址,将它传给形参指针变量x,这时x就指向a[0]。x+m是a[m]元素的地址。设i和j以及p都是指针变量,用他们指向有关元素。i的初值为x,j的初值为x+n-1。使
*i
和*j
交换就是使a[i]和a[j]交换
#include<stdio.h>
void inv(int *x,int n);
int main()
{
int a[10] = {3,7,9,11,0,6,7,5,4,2};
int i;
printf("The original array:\n");
for(i = 0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
inv(a,10);
printf("The array has been inverted:\n");
for(i = 0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
return 0;
}
void inv(int *x,int n)
{
int *p,*i,*j,temp,m = (n-1)/2;
i = x;
j = x+n-1;
p = x+m;
for(i = x;i<=p;i++,j--)
{
temp = *i;
*i = *j;
*j = temp;
}
return ;
}
方法3:利用指针变量作为实参
#include<stdio.h>
void inv(int *x,int n);
int main()
{
int a[10] ;
int *p = a;
int i;
printf("The original array:\n");
for(i = 0;i<10;i++,p++)
scanf("%d",p);
printf("\n");
p = a;
inv(p,10);
printf("The array has been inverted:\n");
for(p = a;p<a+10;p++)
printf("%d ",*p);
printf("\n");
return 0;
}
void inv(int *x,int n)
{
int *p,*i,*j,temp,m = (n-1)/2;
i = x;
j = x+n-1;
p = x+m;
for(i = x;i<=p;i++,j--)
{
temp = *i;
*i = *j;
*j = temp;
}
return ;
}
例:用指针方法对10个整数按由大到小的顺序排序
思路:在主函数中定义数组a存放10个整数,定义
int *
型指针变量p并指向a[0]。定义函数sort使数组a中的元素按由大到小的顺序排序,在主函数中调用sort函数,用指针变量p作为实参,sort函数的形参用数组名,用选择法进行排序。
#include<stdio.h>
void sort(int x[],int n);
int main()
{
int a[10] ;
int *p = a; //指针变量指向a[0]
int i;
printf("input 10 numbers:\n");
for(i = 0;i<10;i++,p++)
scanf("%d",p);
printf("\n");
p = a; //指针变量重新指向数组首地址
sort(p,10);
for(p = a;p<a+10;p++)//输出排序后的10个元素
printf("%d ",*p);
printf("\n");
return 0;
}
void sort(int x[],int n)
{
int i,j,k,t;
for(i = 0;i<n-1;i++)
{
k = i;
for(j = i+1;j<n;j++)
{
if(x[j]>x[k])
{
k = j;
}
if(k!=i)
{
t = x[i];
x[i] = x[k];
x[k] = t;
}
}
}
return ;
}
sort函数可以改为指针变量等价于:
void sort(int *x,int n)
{
int i,j,k,t;
for(i = 0;i<n-1;i++)
{
k = i;
for(j = i+1;j<n;j++)
{
if(*(x+j)>*(x+k))
{
k = j;
}
if(k!=i)
{
t = *(x+i);
*(x+i)= *(x+k);
*(x+k)= t;
}
}
}
return ;
}
总结
以上就是今天学习的内容,本文介绍了通过指针引用数组的几种方式,常用的还需要多多练习。