C语言--通过指针引用数组

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.通过指针引用数组元素

引用数组的方式

  1. 直接引用—a[i]
  2. 间接引用—*(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 ;
 }

总结

以上就是今天学习的内容,本文介绍了通过指针引用数组的几种方式,常用的还需要多多练习。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

最没脑子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值