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

2023-10-26

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 ;
 }

总结

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言--通过指针引用数组 的相关文章

随机推荐

  • 使用V C ++ 向.exe文件中写入资源

    使用V C 向 exe文件中写入资源 BOOL result HANDLE hFile CreateFile c test htm GENERIC READ FILE SHARE READ NULL OPEN EXISTING FILE A
  • Linux虚拟机增加内存后扩展swap空间( insufficient virtual memory,please increase swap space)

    虚拟内存 swap 的基本概念 虚拟内存 swap 就是将硬盘规划出一个区 间 让内存的数据可以经由硬盘来读取 swap工作原理是 当物理内存不够时 则某些在内存当中所占的程序会暂时被 移动到 swap 中 让物理内存可以被更需要的程序来优
  • 在Qt项目中添加pri文件

    在Qt项目框架里 很多人都喜欢用pri文件来管理项目 那么今天就来讲讲这个pri文件 目录 前言 创建pri文件的步骤 一 创建Qt项目 二 创建pri空文件 三 调试 前言 一般我们创建Qt项目工程的时候 都是直接把所有的项目 头文件和资
  • Qt 添加其他目录的资源文件

    添加App目录资源文件的方法 首先 添加 qrc文件 Add New xxx qrc 其次 在qrc文件上添加存在的目录 或者文件 添加其他目录资源文件的方法 如下图步骤
  • 网易一面,痛失30K:为啥用阻塞队列,list不行吗?

    程序员的成长之路 互联网 程序员 技术 资料共享 关注 阅读本文大概需要 5 5 分钟 来自 技术自由圈 本文目录 说在前面 1 什么是阻塞队列 2 主要并发队列关系图 3 阻塞队列和 List Set 的区别是什么 4 阻塞队列和普通Qu
  • Python之time模块

    目录 1 引言 2 时间戳与时间元组 2 1时间戳 2 2时间元组 3 时间格式化 3 1获取格式化的时间 3 2自定义格式化 4 附录 4 1重要函数 4 2参考 1 引言 Python 提供了一个 time模块来格式化时间 在pytho
  • Dockerfile镜像搭建实例+镜像构建LNMP

    Dockerfile镜像搭建实例 镜像构建LNMP 文章目录 Dockerfile镜像搭建实例 镜像构建LNMP 构建SSH镜像 构建Systemctl镜像 构建tomcat镜像 搭建LNMP 环境准备 拉取centos 7镜像 自定义网络
  • SpringBoot整合Mybatis实现商品评分

    前言 当今的电商平台越来越依赖于用户评分 以确定一个商品在市场中的竞争力和口碑 而SpringBoot整合Mybatis plus是非常适用于这一功能的框架 本文将介绍在SpringBoot应用中整合Mybatis plus框架 实现对商品
  • Android 沉浸式状态栏与隐藏导航栏

    一般我们在Android的APP开发中 APP的界面如下 可以看到 有状态栏 ActionBar ToolBar 导航栏等 一般来说 APP实现沉浸式有三种需求 沉浸式状态栏 隐藏导航栏 APP全屏 沉浸式状态栏是指状态栏与ActionBa
  • 长文详解HiveSQL执行计划

    本文目录 一 前言二 SQL的执行计划 2 1 explain 的用法 2 2 explain 的使用场景 案例一 join 语句会过滤 null 的值吗 案例二 group by 分组语句会进行排序吗 案例三 哪条sql执行效率高呢 案例
  • 人工测试之代码检查、走查与评审

    人工测试方法 代码检查 代码走查 桌面检查 同行评审 代码检查与代码走查的联系 1 要求人们组成一个小组来完阅读或直观检查特定的程序 找出错误 但不必改正错误 2 都是对过去桌面检查过程 在提交测试前 由程序员阅读自己程序的过程 的改进 3
  • Mac m1上使用docker搭建C++开发调试环境

    说明 因为mac上虚拟机都不太好用 有些还收费 故使用docker来搭建虚拟机 况且我的Mac是m1芯片 用的是arm架构 虚拟机更是少 搭建本机与虚拟机互通 mac上docker与linux不同 mac上实际上内部是建了个linux的虚拟
  • 在编写Java程序时,图片不显示的问题

    可能有几种原因导致图片无法正常显示 图片路径不正确 如果图片的路径错误或不存在 程序将无法找到图片并将其加载到内存中 在Java中 可以使用相对路径或绝对路径来指定图片路径 应根据实际情况进行调整 图片格式不支持 如果图片格式不受Java支
  • JAVA--泛型

    一 概念 1 定义 在定义类或接口通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型 2 引入 从JDK1 5后 Java引入 参数化类型 概念 即在创建集合时指定可以再指定元素类型 3 优点 a 解决元素存储的安全性问题
  • Linux中怎么创建文件?

    众所周知 在Windows系统中可以直接右键新建文件 而在Linux系统中 想要创建文件并非易事 需要通过执行命令来完成 那么Linux系统中创建文件常用的方法有哪些 本文为大家介绍一下Linux系统下创建文件的8种方法 快来了解一下吧 1
  • Impala与Hive的比较

    http tech uc cn p 1803
  • 达梦数据库报“网络通信异常”分析解决

    前言 达梦数据库在通过程序插入具有BLOB字段的记录时 非通过SQL直接插入 报 通信异常 通过更换达梦库驱动包解决 问题 在一个项目现场 在进行数据导入时 总时报 网络通信异常 19 08 56 ERROR Application exc
  • element-ui下的table的鼠标悬浮颜色修改

    对于表格 有时候仅仅用作展示效果 所以不需要悬浮之类的效果 但是官网上又没有修改悬浮颜色的 这里使用类名样式覆盖 v deep el table enable row hover el table body tr hover gt td b
  • dns服务器和域控制器在同一台计算机上安装.( ),Certkiller.COM有一个ActiveDirectory域控制器。所有域控制器配置为DNS服务器和WindowsServer2008安装。只...

    YouareimplementinganASP NETDynamicDataWebsite TheWebsiteincludesadatacontextthatenablesautomaticscaffoldingforalltablesi
  • C语言--通过指针引用数组

    C语言 通过指针引用数组 文章目录 C语言 通过指针引用数组 前言 1 引用数组元素时指针的运算 2 通过指针引用数组元素 引用数组的方式 示例 用三种方法输出元素中的全部元素 方法一 直接引用a i 方法二 间接引用 通过数组名计算数组元