指针与数组---指针与一维数组的关系

news2024/11/15 8:34:00

        C语言的高效得益于它指针功能的强大。然而C语言中的指针和数组的关系似乎很“纠结”,让人爱恨交织。指向数组的指针变量、指针数组等,似乎总是“你中有我,我中有你”。

目录

一、数组名的特殊意义及其在访问数组元素中的作用

二、指针运算的特殊性及其在访问数组元素中的作用

三、数组和指针作为函数参数进行模拟按引用调用中的相似性


一、数组名的特俗意义及其在访问数组元素中的作用

        一旦给出数组的定义,编译系统就会为其在内存中分配固定的存储单元。相应地,数组的首地址也就确定了。数组元素在内存中是连续存放的,C语言中的数组名有特殊的含义,它代表存放数组元素的连续空间的首地址,即指向数组中第一个元素的指针常量。因此,数组元素既可用下标法也可用指针法来引用。

例题:例程,分别使用下标法指针法

 下标法:

#include <stdio.h>

int main(void)
{
	int a[5],i;
	printf("Input five numbers:");
	for(i=0;i<5;i++)
	{
		scanf("%d",&a[i]);
	}
	for(i=0;i<5;i++)
	{
		printf("%4d",a[i]);
	}
	printf("\n");
	return 0;
} 

        假设数组a的首地址为0x0022ff40,每个元素占4个字节内存。如下图1-1所示,数组a的5个数组元素将占据从0x0022ff40到0x0022ff53的共20个字节的存储空间,元素a[0]的地址为0x0022ff40,元素a[1]的地址为0x0022ff44,以此类推,元素a[4]的地址为0x0022ff50。

因数组名a代表数组的首地址,即元素a[0]的地址(&a[0]),所以表达式a+1表示首地址后下一个元素的地址,即数组中的第2个元素即下标为1的元素a[1]的地址(&a[1])。由此可知,表达式a+i代表数组中下标为i的元素a[i]的地址(&a[i])。已知数组元素的地址后,就可以通过间接寻址来引用数组中的元素了。例如,*a或*(a+0)表示取首地址a所指的存储单元中的内容,即元素a[0],*(a+0)表示取出首地址a所指的存储单元中的内容,即元素a[0],*(a+i)表示取出首地址元素后第i个元素的内容,即下标为i的元素a[i]。

因此程序可以使用如下方法。

指针法:

#include <stdio.h>

int main(void)
{
	int a[5];
	int i;
	for(i=0;i<5;i++)
	{
		scanf("%d",a+i);
	}
	for(i=0;i<5;i++)
	{
		printf("%4d",*(a+i));
	}
	return 0;
}

数组元素之所以能通过这种方法来引用,是因为数组的下标运算符[ ]实际上就是以指针作为其操作数的。例如,a[i]被编译器解释为表达式*(a+i),即表示引用数组首地址所指元素后面的第i个元素,而&a[i]表示取数组a的第i+1个元素的地址,它等价于指针表达式a+i。

二、指针运算的特殊性及其在访问数组元素中的作用

        指针的算数运算和关系运算常常是针对数组元素而言的。因数组在内存中是连续存放的,所以指向同一数组中不同元素的两个指针的关系运算常用于比较它们所指元素在数组中的前后位置关系。指针的算数运算(如增1和减1)则常用于移动指针的指向,使其指向数组中的其他元素。当然,仅当运算结果仍指向同一数组中的元素时,指针的算术运算才有意义。

        如果定义了一个指向整型数据的指针变量p,并使其值为数组a的可访问数组a的元素,如下图所示。注意,p+1与p++本质上是两个不同的操作,因为没有对p进行赋值操作,所以p+1并不改变当前指针的指向,p仍然指向原来指向的元素,而p++相当于执行了p=p+sizeof(p的基类型),因此p执行了赋值操作而改变了指针p的指向,此外该操作并不是将指针p向前移动了一个字节,而是将指针变量p向前移动了一个元素位置,即指向了下一个元素。

 

 

        此外,p++并非将指针变量p的值简单的加1,而是加上1*sizeof(基类型)个字节。例如,如果基类型为int,而sizeof(int)为4的话,那么p++就相当于p加上了4个字节。

采用 通过移动指针变量p来引用数组元素的方法,可将上例程序进行如下修改。

#include <stdio.h>
int main(void)
{
	int a[5],*p;
	printf("Input five numbers:");
	for(p=a;p<a+5;p++)
	{
		scanf("%d",p);
	}
	for(p=a;p<a+5;p++)
	{
		printf("%4d",*p);
	}
	printf("\n");
	return 0;
 } 

        因a被解释为一个指向a[0]的整型指针常量,所以执行第6行for语句中的赋值操作p=a后,指针变量p也指向了a[0],于时就可以通过移动指针变量p来依次应用数组a的元素了。

        由于指针变量p指向数组的首地址&a[0],所以*p表示取出p所指向的内存单元中的内容,即元素a[0]的值,p+1指向当前指针所指元素的下一个元素,p+i指向当前指针下面的第i个元素,*(p+i)表示取出p+i所指向的内存单元中的内容,即元素a[i]的值。

        注意,虽然a和p的值都是数组的首地址,但不能像使用指针变量p那样对数组名a执行增1减1操作。这是因为p是指针变量,可通过赋值操作改变它的值,使p指向数组中的其他元素,而数组名a是指针常量,代表一个地址常量,其值是不能被改变的。

        指针也可用下标形式表示。采用指针的下标表示法:

 #include <stdio.h>
 int main(void)
 {
 	int a[5],*p=NULL,i;
 	printf("Input five numbers:");
 	p=a;
 	for(i=0;i<5;i++)
 	{
 		scanf("%d",&p[i]);
	 }
	p=a;
	for(i=0;i<5;i++)
	{
		printf("%4d",p[i]);
	}
	printf("\n");
	for(i=0;i<5;i++)
	{
		printf("%4d",a[i]);
	}
	printf("\n");
	return 0;
 }

可见,用数组实现的操作也可用指针来实现。由于增1运算的执行效率很高,所以利用指针增1运算实现指针的移动,省去了每寻址一个数组元素都要进行的指针算数运算。因此,在上面的4中方法中,第2中方法的执行效率最高,而其他计中方法的执行效率是一样的。虽然用指针编程比数组编程效率高,但使用数组编程的方法更加直观和容易理解。

三、数组和指针作为函数参数进行模拟按引用调用中的相似性

        用数组名和用指向一维数组的指针变量作函数实参,向被调函数传递的都是数组的起始地址,都是模拟按引用调用。一维数组作函数形参时,因为它只起到接收数组的起始地址的作用,所以会发生数组类型到指针类型的隐式转换,即使将形参声明为一维数组,它也将退化为指针,系统仅仅为其分配指针所占的内存空间,并不为形参数组分配额外的存储空间,而是让形参数组共享实参数组所占的存储空间。因此用一维数组作函数形参与用指针变量作函数形参本质上是一样的,因为它们接收的都是数组的起始地址,都需要按此地址对主调函数中的实参数组元素进行间接寻址,因此在被调函数中既能以下标的形式也能以指针的形式来访问数组元素。需要注意的是,数组和指针并非在所有情况下都是等同的。例如,sizeof(数组名)和sizeof(指针变量名)就是不可互换的。

例题:演示数组和指针变量作为函数参数

方法1:被调函数的形参声明为数组类型,用下标法访问数组元素。

 void InputArray(int a[],int n);
 void OutputArray(int a[],int n);
 void InputArray(int a[],int n)
 {
 	int i;
 	for(i=0;i<n;i++)
 	{
 		scanf("%d",&a[i]);
	 }
 }
 
 void OutputArray(int a[],int n)
 {
 	int i;
 	for(i=0;i<;i++)
 	{
 		printf("%4d",a[i]);
	 }
	 printf("\n");
 }

方法2:被调函数的形参声明为指针变量,用指针法访问数组元素。

void InputArray(int *pa,int n);
 void OutputArray(int *pa,int n);
 void InputArray(int *pa,int n)
 {
 	int i;
 	for(i=0;i<n;i++,pa++)
 	{
 		scanf("%d",pa);
	 }
 }
 
 void OutputArray(int *pa,int n)
 {
 	int i;
 	for(i=0;i<n;i++,pa++)
 	{
 		printf("%4d",*pa);
	 }
	 printf("\n");
 }

方法3:被调函数的形参声明为数组类型,用指针法访问数组元素。

void InputArray(int a[],int n);
void OutputArary(int a[],int n);
void InputArray(int a[],int n)
{
	int i;
	for(i=0;i<n;i++)
	{
		scanf("%d",a+i);
	}
}

void OutputArray(int a[],int n)
{
	int i;
	for(i=0;i<n;i++)
	{
		printf("%4d",*(a+i));
	}
	printf("\n");
}
 

方法4:被调函数的形参声明为指针变量,用下标法访问数组元素。

void InputArray(int *pa,int n);
void OutputArray(int *pa,int n);
void InputArray(int *pa,int n)
{
	int i;
	for(i=0;i<n;i++)
	{
		scanf("%d",&pa[i]);
	}
}

void OutputArray(int *pa,int n)
{
	int i;
	for(i=0;i<n;i++)
	{
		printf("%d",pa[i]);
	}
	printf("\n");
}

在主函数中,都可以用数组名作为函数实参。

#include <stdio.h>

int main(void)
{
	int a[5];
	printf("Input five numbers:");
	InputArray(a,5);
	OutputArray(a,5);
	return 0;
}
 //也可以用指针变量作为函数参数。
int main(void)
{
	int a[5];
	int *p=a;
	printf("Input five numbers:");
	InputArray(p,5);
	OutputArray(p,5);
	return 0;
}
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/658692.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux常用指令和知识(1)

目录 ls cd pwd 相对路径&绝对路径&特殊路径符 mkdir touch-cat-more cp-mv-rm which-find grep-wc 管道符 | echo 重定向符 tail &#x1f636;‍&#x1f32b;️&#x1f618;创作不易, 多多支持 前言: 我们学习的Linux命令, 其实他们的本体就是一个个…

ctf 逆向 专题题解

本文的目标是&#xff0c;记录一些不具备通用性的&#xff0c;或者比较进阶的题目。之前的另一篇文章则用于记录一些基础知识和通用性较强的基本手法。 文章目录 跨科题目buu fungame&#xff1a;reverse与pwn的结合reverseweb 反跟踪Easyhook&#xff1a;hook例题 vm类型总结一…

我的创作纪念日——512

机缘 没想到不知不觉在CSDN创作就512天了&#xff0c;想到一开始就仅仅想在CSDN记笔记&#xff0c;到现在成为一个小博主&#xff0c;认识到了很多志同道合的伙伴&#xff0c;中间创作我也曾经懒惰过&#xff0c;放弃过&#xff0c;但我一次又一次重新进行创作&#xff0c;虽然…

AcWing801: 二进制中1的个数(两种方法详解)

原题引出 方法一&#xff1a;使用lowbit 算法的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)&#xff0c;使用lowbit操作&#xff0c;每次操作截取一个数字的最后一个1后面的所有位&#xff0c;每次减去lowbit得到的数字&#xff0c;直到数字减到0&#xff0c;就得到了最终…

【MySQL】选择专题(七)

文章目录 选择题选择题 在关系R ( R # , RN , S # )和S ( S # , SN , SD )中, R 的主码是R # , S 的主码是S #,则S#在R 中称为( A )。 A 外码 B 候选码 C 主码 D 超码 设关系R和S的属性个数分别为2和3,那么等价于( B )。 A. σ1<2(RS) B. σ1<4(RS) C. σ1<2(R…

我们世界中的计算机——从大师视角诠释计算常识

计算机和通信系统&#xff0c;以及由它们所实现的许多事物遍布我们周围。其中一些在日常生活中随处可见&#xff0c;比如笔记本电脑、手机和互联网。今天&#xff0c;在任何公共场所&#xff0c;都会看到许多人在使用手机查询交通路线、购物以及和朋友聊天。与此同时&#xff0…

【大数据】大数据相关概念

文章目录 大数据&#xff1a;一种规模大到在获取、存储、管理、分析方面大大超出了传统数据库软件工具能力范围的数据集合&#xff0c;具有海量的数据规模、快速的数据流转、多样的数据类型以及价值密度四大特征。Hadoop&#xff1a;是一个能够对大量数据进行分布式处理的软件框…

15-3.自定义组件的生命周期函数

目录 1 组件自身的生命周期函数 1.1 使用lifetimes声明生命周期函数 1.2 不使用lifetimes声明生命周期函数 2 组件所在页面的生命周期函数 1 组件自身的生命周期函数 created 组件实例刚刚被创建后执行&#xff0c;可以理解为 html模板刚刚搞好attached 组件被放入节…

万物的算法日记|第六天

笔者自述&#xff1a; 一直有一个声音也一直能听到身边的大佬经常说&#xff0c;要把算法学习搞好&#xff0c;一定要重视平时的算法学习&#xff0c;虽然每天也在学算法&#xff0c;但是感觉自己一直在假装努力表面功夫骗了自己&#xff0c;没有规划好自己的算法学习和总结&am…

DJ4-2 数据报网络和虚电路网络

目录 一、连接和无连接服务 二、数据报网络 1、数据报网络的转发表 2、数据报网络的特点 三、虚电路网络 (Virtual Circuits)* 1、虚电路网络的工作方式 2、虚电路网络的特点 一、连接和无连接服务 任何网络中的网络层只会提供两种服务之一&#xff0c;不会同时提供 数…

Mysql数据库之事务(山高水远,他日江湖再见)

文章目录 一、事务的概念二、事务的ACID特点1.原子性&#xff08;Atomicity&#xff09;2.一致性&#xff08;Consistency&#xff09;3.隔离性&#xff08;lsolation&#xff09;4.持久性&#xff08;Durability) 三、并发访问表的一致性问题和事务的隔离级别1.并发访问表的一…

融合模型stacking14条经验总结和5个成功案例(互联网最全,硬核收藏)_机器学习_人工智能_模型竞赛_论文参考

我看了很多关于融合模型stacking文章&#xff0c;很多作者倾向于赞美融合模型stacking&#xff0c;对其缺点轻描淡写&#xff0c;这容易误导初学者。一叶障目就是这意思。 我的很多学员喜欢用融合模型作为论文或专利创新点&#xff0c;这是一个热门技术。 最近有个同学在论文…

设计模式之单例模式笔记

设计模式之单例模式笔记 说明Singleton(单例)目录单例模式之饿汉式-静态成员变量写法测试类 单例模式之饿汉式-静态代码块写法测试类 单例模式之懒汉式-线程不安全写法和线程安全写法测试类 单例模式之懒汉式-双重检查锁方式(推荐使用的方式)单例模式之懒汉式-静态内部类方式(推…

Mysql数据库之存储引擎(羡慕她人,不如提升自己)

一、存储引擎概念 MySQL中的数据用各种不同的技术存储在文件中&#xff0c;每一种技术都使用不同的存储机制、索引技巧、锁定水平并最终提供不同的功能和能力&#xff0c;这些不同的技术以及配套的功能在MySQL中称为存储引擎。 存储引擎是MySQL将数据存储在文件系统中的存储方…

ELK日志收集系统简述

一、概述 &#xff08;一&#xff09;ELK由三个组件构成 ELK是三个开源软件的缩写&#xff0c;分别是Elasticsearch、Logstash、Kibana ELK 架构基本组成 &#xff08;二&#xff09;作用 1、日志收集 2、日志分析 3、日志可视化 &#xff08;三&#xff09;为什么使用EL…

计网之应用层

因特网协议概述 常用协议应用层HTTP&#xff08;超文本传输协议&#xff09;、FTP&#xff08;文件传输协议&#xff09;、SMTP&#xff08;简单邮件传输协议&#xff09;、DNS&#xff08;域名系统&#xff09;、DHCP&#xff08;动态主机配置协议&#xff09;、SNMP&#xff…

15-6.自定义组件的代码共享

在微信小程序中使用 behaviors 进行代码共享&#xff0c;功能类似于vue的mixins 每个behavior可以包含一组属性、数据、生命周期函数和方法 每个组件可以引用多个behavior&#xff0c;behavior也可以引用其他的behavior 目录 1 创建behavior 2 使用behavior 3 behavio…

机器学习融合模型stacking14条经验总结和5个成功案例(互联网最全,硬核收藏)

我看了很多关于融合模型stacking文章&#xff0c;很多作者倾向于赞美融合模型stacking&#xff0c;对其缺点轻描淡写&#xff0c;这容易误导初学者。一叶障目就是这意思。 我的很多学员喜欢用融合模型作为论文或专利创新点&#xff0c;这是一个热门技术。 最近有个同学在论文…

MySQL:七种 SQL JOINS 的实现(图文详解)

MySQL&#xff1a;7种SQL JOINS的实现 前言一、图示表示二、代码举例1、INNER JOIN&#xff08;内连接&#xff09;2、LEFT JOIN&#xff08;左连接&#xff09;3、RIGHT JOIN&#xff08;右连接&#xff09;4、OUTER JOIN&#xff08;全连接&#xff09;5、LEFT EXCLUDING JOI…

微信小程序入门学习02-TDesign中的自定义组件

目录 1 显示文本2 自定义组件3 变量定义4 值绑定总结 我们上一篇讲解了TDesign模板的基本用法&#xff0c;如何开始阅读模板。本篇我们讲解一下自定义组件的用法。 1 显示文本 官方模板在顶部除了显示图片外&#xff0c;还显示了一段文字介绍。文字是嵌套在容器组件里&#xf…