C语言 用数组名作函数参数

news2025/1/10 11:02:42

当用数组名作函数参数时,如果形参数组中各元素的值发生变化,实参数组元素的值随之变化。

1.数组元素做实参的情况:

如果已经定义一个函数,其原型为

void swap(int x,int y);

假设函数的作用是将两个形参(x,y)进行交换,现在有以下的函数调用:

swap (a[0],a[1]);

用数组元素做实参的情况,与用变量作实参的情况一样,是“值传递”方式,将a[0]和a[1]的值单向传递给x和y。当x和y的值改变时a[0]和a[1]的值并不改变。

2.数组名作函数形参的情况:

实参数组名代表该数组首元素的地址,而形参是用来接收从实参传递过来的数组首元素的地址的。因此,形参应该是一个指针变量(只有指针变量才能存放地址)。实际上,C编译都是将形参数组名作为指针变量来处理的。

例如:定义一个函数fun,fun函数的形参写成数组的形式:

void fun(int arr[],int n);

但在程序编译时是将arr按指针变量来处理的,相当于函数fun应写成:

void fun(int *arr,int n);

在该函数被调用时,系统会在fun函数中建立一个指针变量arr,用来存放从主调函数传递过来的实参数组元素的地址。如果在fun函数中用运算符sizeof测定arr所占字节数,可以发现sizeof(arr)的值为4。这就证明了系统是把arr作为指针变量来处理的。例如:

void fun(int arr[], int n)
{
	int s = sizeof(arr);
	printf("arr所占字节数为:%d\n", s);
}
int main()
{
	int brr[10];
	fun(brr, 10);
	return 0;
	
}

运行结果:
在这里插入图片描述
当arr接收了实参数组的首元素地址后,arr就指向实参数组的首元素,也就是指向了brr[0]。因此,arr就是brr[0]。arr+1指向brr[1],arr+2指向brr[2],arr+3指向brr[3].也就是说,*(arr+1)*(arr+2)*(arr+3)分别是brr[1],brr[2],brr[3]。*(arr+i)arr[i]是无条件等价的。因此在调用函数期间,arr[0]和arr以及brr[0]都代表数组brr序号为0的元素。

【注意】

实参数组名代表一个固定的地址,或者说是指针常量,但形参数组名并不是一个固定的地址,而是按指针变量处理。

在函数调用进行虚实结合后,形参的值就是实参数组首元素的地址。在函数指向期间,它可以在被赋值。

【例】定义一个数组名作形参的函数,通过调用这个函数改变实参数组的值

int fun(int arr[], int n)
{
	//int s = sizeof(arr);
	//printf("arr所占字节数为:%d\n", s);

	int t;
	t = arr[1];
	arr[1] = arr[2];
	arr[2] = t;

	return arr[0];
}
int main()
{
	int brr[10] = { 1,3,2,4,5,6,7,8,9,10 };
	
	fun(brr, 10);

	for (int i = 0; i < 10; i++)
	{
		printf("%d  ", brr[i]);
	}

	return 0;
	
}

运行结果:

在这里插入图片描述

常用这种方法通过调用一个函数来改变实参数组的值。

3.变量名做函数参数和用数组名做函数参数的比较

(1)当实参类型是变量名时,要求形参的类型也是变量名,通过形参传递的信息是变量的值,通过函数调用不能改变实参变量的值。

(2)当实参类型是数组名时,要求形参的类型是数组名或者指针变量,通过形参传递的信息是实参数组首元素的地址,通过函数调用能改变实参变量的值。

说明:C语言调用函数时虚实结合的方法都是采用“值传递”方式,当用变量名作为函数参数时传递的是变量的值。当用数组名作函数参数时,由于数组名代表的是数组首元素地址,因此传递的值是地址,所以要求形参为指针变量。

4.数组名和指针变量作为函数的形参

在C语言中用下标法和指针法都可以访问一个数组(如果有一个数组a,则a[i]和*(a+i)无条件等价)。用数组名作形参,以便于实参数组对应,比较直观便于理解。从应用的角度看,用户可以认为有一个形参数组,它从实参数组那里得到起始地址,因此形参数组与实参数组共占同一段内存单元,在调用函数期间,如果改变了形参数组的值,也就是改变了实参数组的值。在主调函数中就可以利用这些已经改变的值。

【例】将数组a中n个整数按相反顺序存放,用一个函数inv来实现交换。实参用数组名a,形参可用数组名,也可用指针变量。

void inv(int x[], int n)//形参x是数组名
{
	int temp;
	int m = (n - 1) / 2;
	for (int i = 0; i <= m; i++)
	{
		int j = n - 1 - i;
		//把x[i]和x[j]交换
		temp = x[i];
		x[i] = x[j];
		x[j] = temp;
	}
	return;
}
int main()
{
	int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
	inv(a, 10);//调用inv函数进行交换
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", a[i]);
	}
}

运行结果:
在这里插入图片描述
结果分析:
在main主函数中定义整型数组a,并赋初值。函数inv的形参数组名为x。在定义inv函数时,可以不指定形参数组x的大小(元素个数)。因为形参数组名实际上是一个指针变量,并不是真的开辟一个数组空间(定义实参数组时必须指定数组大小,因为要开辟相应的存储空间)。inv函数的形参n用来接收需要处理的元素的个数。在main函数中有函数调用语句inv(a,10);,表示要求将a数组的10个元素颠倒排列。如果改为inv(a,5);,则表示要求将a数组的前5个元素颠倒排列,此时函数inv只处理5个数组元素。函数inv中的m是i值得上限,当i<=m时,循环继续执行;当i>m时,则结束循环过程。

改写代码,将函数inv中得形参x改成指针变量:

void inv(int *x, int n)
{
	int temp;//定义一个中间变量,用来交换两个变量得值
	int m = (n - 1) / 2;
	int* p;//定义一个指针变量p
	p = x + m;//p指向a[m]元素的地址
	int* i;//定义一个指针变量i
	i = x;//i指向数组首元素的地址
	int* j;//定义一个指针变量j
	j = x + n - 1;//j指向数组最后一个元素的地址
	for (i=x;i<=p;i++,j--)
	{
	//交换i和j所指向的数组元素的值
		temp = *i;
		*i = *j;
		*j = temp;
	}
	return;
}
int main()
{
	int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
	inv(a, 10);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", a[i]);
	}
}

改写后,函数inv中得形参有数组名x[]变为了指针变量*x,相应的实参仍是数组名a,即数组a首元素的地址,将它传给形参指针变量x,这时x就指向a[0]。x+m是a[m]元素得地址。设i和j以及p都是指针变量,用它们指向有关元素。交换i和j所指向的数组元素的值,实际上就是交换a[i]和a[j]的值。

运行结果:
在这里插入图片描述

5.归纳分析

(1)形参和实参都用数组名
int fun(int x[],int n)
{
	.
	.
	.
}
int main()
{
	int a[10];
	.
	.
	.
	fun(a,10);
	.
	.
	.
	return 0;
}

由于形参数组名x接收了实参数组名首元素a[0]的地址,因此可以认为在函数调用期间,形参数组和实参数组共用一段内存单元,如图所示:
在这里插入图片描述

(2)实参用作数组名,形参用指针变量
void fun(int *x,int n)
{
	.
	.
	.
}
int main()
{
	int a[10];
	.
	.
	.
	fun(a,10);
	.
	.
	.
	return 0;
}

实参a为数组名,形参x为int *型的指针变量,调用函数开始后,形参x指向a[0],即x=&a[0],通过x值的改变,可以指向a数组的任一元素,如图所示:
在这里插入图片描述

(3)实参形参都用指针变量
void fun(int *x,int n)
{
	.
	.
	.
}
int main()
{
	int a[10];
	int *p=a;
	.
	.
	.
	fun(a,10)
	.
	.
	.
	return 0;
}

实参p和形参x都是int*型指针变量。先使用实参指针变量p指向数组元素a[0],p的值是&a[0]。然后将p的值传给形参变量x,x的初始值也是&a[0],通过x的值的改变可以使x指向数组a的任一元素,如图所示:
在这里插入图片描述

(4)实参为指针变量,形参为数组名
void fun(int x[],int n)
{
	.
	.
	.
}
int main()
{
	int a[10];
	int *p=a;
	.
	.
	.
	fun(p,10);
	.
	.
	.
	return 0;
}

实参p作为指针变量,它指向a[0]。形参为数组名x,编译系统把x作为指针变量处理,将a[0]的地址传给形参x,使x也指向a[0]。也可以理解为形参数组x和a数组共用同一内存单元,在函数执行过程中可以使x[i]的值发生变化,而x[i]就是a[i]。这样主函数可以使用改变了的数组元素值,如图所示:
在这里插入图片描述
【例】指针变量做实参

void inv(int* x, int n)
{
	int temp;
	int m = (n - 1) / 2;
	int* p;
	p = x + m;
	int* i;
	i = x;
	int* j;
	j = x + n - 1;
	for (i = x; i <= p; i++, j--)
	{
		temp = *i;
		*i = *j;
		*j = temp;
	}
	return;
}
int main()
{
	int a[10];
	int* p = a;//指针变量p指向a[0]
	//输入数组a的元素
	for (int i = 0; i < 10; i++, p++)
	{
		scanf("%d", p);
	}
	p = a;//将指针变量p重新指向a[0]
	inv(p, 10);//调用inv函数,实参是变量p
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

运行结果:
在这里插入图片描述
上面的main函数中的指针变量p是有确定值的。如果在main函数中不设数组,只设指针变量,就会出错。假如把主函数修改如下:

void inv(int* x, int n)
{
	int temp;
	int m = (n - 1) / 2;
	int* p;
	p = x + m;
	int* i;
	i = x;
	int* j;
	j = x + n - 1;
	for (i = x; i <= p; i++, j--)
	{
		temp = *i;
		*i = *j;
		*j = temp;
	}
	return;
}
int main()
{
	int a[10];
	int* p;
	//输入数组a的元素
	for (int i = 0; i < 10; i++)
	{
		scanf("%d", p+i);
	}

	inv(p, 10);//调用inv函数,实参是变量p
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p+i));
	}
	return 0;
}

编译时会出错,原因是指针变量p没有确定值,谈不上指向哪个变量:
在这里插入图片描述
因此,下面这样的使用是不正确的:

void fun(int x[],int n)
{
	.
	.
	.
}
int main()
{
	int *p;
	.
	.
	.
	fun(p,10);
}

【注意】如果指针变量做实参,必须先使指针变量有确定的值,指向一个已经定义的对象。

以上4种方法,实质上都是地址的传递。其中(3)和(4)两种只是形式上不同,实际上形参都是使用的指针变量。

【例】使用指针方法对10个整数按由大到小的顺序排列。
【思路】在主函数种定义数组a存放10个整数,定义int*型指针变量p指向a[0]。定义函数sort使数组a种的元素由大到小的顺序排列。在主函数中调用sort函数,用指针变量p作实参。sort函数的形参用数组名。用选择法进行排序。

//定义sort函数,x是形参数组名
void sort(int x[], int n)
{
	int temp;
	for (int i = 0; i < n-1; i++)
	{
		for (int j = i + 1; j < n; j++)
		{
			if (x[j]>x[i])
			{
				temp = x[i];
				x[i] = x[j];
				x[j] = temp;
			}
		}
	}
}
int main()
{
	int a[10];
	int* p = a;//指针变量p指向a[0]
	//输入10个整数
	for (int i = 0; i < 10; i++)
	{
		scanf("%d", p++);
	}
	p = a;//指针变量p重新指向a[0]
	sort(p, 10);//调用sort函数
	//输出排序后的10个数组元素
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *p);
		p++;
	}
	return 0;
}

运行结果:
在这里插入图片描述
如果sort函数中将x定义为指针变量,在函数中仍可以用x[i]和x[j]这样的形式表示数组元素,它就是x+i和x+j所指的数组元素。

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

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

相关文章

云运维工具

企业通常寻找具有成本效益的方法来优化创收&#xff0c;维护物理基础架构以托管服务器和应用程序以提供服务交付需要巨大的空间和前期资金&#xff0c;最重要的是&#xff0c;物理基础设施会产生额外的运营支出以进行定期维护&#xff0c;这对收入造成了沉重的损失。 云使企业…

降维 — PCA 真的能改善分类结果吗?

一、说明 我遇到了一些关于降维技术的资源。这个主题绝对是最有趣的主题之一&#xff0c;很高兴认为有一些算法能够通过选择仍然代表整个数据集的最重要的特征来减少特征的数量。作者指出的优点之一是这些算法可以改善分类任务的结果。 在这篇文章中&#xff0c;我将使用主成分…

在线免费做分班查询软件:这个制作分班查询系统的平台就可实现

在制作分班查询系统前&#xff0c;作为老师的我们可以先理清制作分班查询系统的意义&#xff01;这几个要点可能很多老师都没有留意过&#xff01; 提高工作效率&#xff1a; 学校每年都需要进行学生的分班工作&#xff0c;如果采用传统的手工方式进行分班查询&#xff0c;会…

防火墙规则分析管理

防火墙规则在高效的网络安全管理中起着至关重要的作用&#xff0c;在添加规则之前&#xff0c;确保提议的新规则不会对网络产生负面影响至关重要。 通过防火墙规则影响分析&#xff0c;安全管理员可以详细了解添加新规则的可能影响&#xff0c;防火墙规则影响分析的一个重要方…

假日购物季已经打响?卖家要提前行动起来啦!

去年&#xff0c;假日购物季比以往任何时候都要早开启&#xff0c;这促使消费者对今年的购物季抱有更多的期待。 根据Optimove最新的消费者调查&#xff0c;有一半的消费者&#xff08;50%&#xff09;计划在11月之前开始他们的假日购物。 当被问及是什么促使他们提前购买时&…

【已解决】安装win7系统,“Windows安装程序无法将Windows配置在此计算机的硬件上运行”

问题&#xff1a; 安装windows7时报错&#xff1a;“Windows安装程序无法将Windows配置在此计算机的硬件上运行” 解决办法&#xff1a; 方法一 shiftF10 调出命令提示行&#xff0c;输入cd oobe 然后再输入msoobe 回车&#xff0c;就可以继续进行下一步了。 如果方法一不…

你知道充电桩控制主板的结构吗?

你知道充电桩控制主板的结构吗? 你是否曾经遇到过电动车行驶途中突然没电的情况?不用担心&#xff0c;解决这个问题的方法之一就是使用充电桩。那么&#xff0c;控制主板是如何控制充电桩的呢?让我们一起来探究一下。 充电桩控制主板由多种元件组成&#xff0c;包括主控芯片…

亚马逊Q2业绩呈井喷式增长,净销售额高达1344亿美元

周四&#xff08;8月3日&#xff09;&#xff0c;亚马逊公布了截至6月30日的2023年第二季度财务业绩。在电商、云和广告销售强劲的推动下&#xff0c;该电商巨头的利润出现井喷式增长。周四盘后交易中&#xff0c;亚马逊股价上涨逾7%。 财报显示&#xff0c;在截至6月30日的这…

linuxARM裸机学习笔记(3)----主频和时钟配置实验

引言&#xff1a;本文主要学习当前linux该如何去配置时钟频率&#xff0c;这也是重中之重。 系统时钟来源&#xff1a; 32.768KHz 晶振是 I.MX6U 的 RTC 时钟源&#xff0c; 24MHz 晶振是 I.MX6U 内核 和其它外设的时钟源 1. 7路PLL时钟源【都是从24MHZ的晶振PLL而来…

LeetCode112. 路径总和

112. 路径总和 文章目录 [112. 路径总和](https://leetcode.cn/problems/path-sum/)一、题目二、题解方法一&#xff1a;递归存储各个路径之和方法二&#xff1a;递归版本2简化版本 一、题目 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 …

回调函数的简单用例

列举项目中一个简单的回调函数用例 ①用MsgInterface_t定义一个结构体s_Lin_MsgInterface&#xff0c;包含两个回调函数成员&#xff1a; ②确定结构体下的回调函数成员的参数&#xff1a; ③传入实参&#xff0c;确定结构体下的回调函数成员的函数名&#xff1a; ④最终回…

docker容器互联详解

目录 docker容器互联详解 一、容器互联概述&#xff1a; 二、案例实验&#xff1a; 1、用户自定义的网络&#xff1a; 2、查看当前的IP信息&#xff1a; 3、启动第三个容器&#xff1a; 4、查看三个容器内部的网络&#xff1a; 5、Ping测试&#xff1a; Ps备注&#x…

CVE-2008-5161

SSH服务器CBC加密模式漏洞 1.描述 一般经过我们的加固&#xff0c;Linux环境中一般都已经采用AES这种算法加密&#xff0c;AES有五种加密模式&#xff08;CBC、ECB、CTR、OCF、CFB&#xff09;&#xff0c;centos7.x系统启动sshd服务后&#xff0c;系统默认选择CBC的机密模式…

div上下左右居中几种方式

div固定宽高 div{width:200px;height:200px;background-color:red; }1、绝对定位&#xff08;常用于登录模块&#xff09; 备注&#xff1a;前提条件div需要有宽高 #html <div class"box"></div> #css .box{ position:absolute/fixed; left:0; right:0…

凯迪正大—SF6泄漏报警装置的主要特点

SF6泄漏报警系统主要特点 ① 系统采用声速原理&#xff0c;可定量、实时在线测量SF6泄漏气体含量&#xff0c;克服了传统测量方法如负电晕放电法和卤素传感器法只能定性判别是否越限的缺陷&#xff0c;能够准确得到气体中SF6含量。 ② 系统采用双差分处理方法&#xff0c;有效…

Linux知识点 -- 进程间通信(一)

Linux知识点 – 进程间通信&#xff08;一&#xff09; 文章目录 Linux知识点 -- 进程间通信&#xff08;一&#xff09;一、了解进程间通信1.进程间通信的必要性2.进程间通信的技术背景3.进程间通信的本质理解4.进程间通信的标准 二、匿名管道1.匿名管道通信的原理2.匿名管道的…

Docker网络模型详解

目录 一、Docker网络基础 1.1、端口映射 1.2、端口暴露 1.3、容器互联 二、Docker网络模式 2.1、Host模式 2.2、container模式 2.3、none模式 2.4、bridge模式 2.5、Overlay模式 网络是激活Docker体系的唯一途径&#xff0c;如果Docker没有比较出色的容器网络&#xff0…

SpringBoot项目-个人博客系统的实现【下】

10.实现强制要求登陆 当用户访问 博客列表页和 博客详情页时, 如果用户当前尚未登陆, 就自动跳转到登陆页面 1.添加拦截器 public class LoginInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletRespon…

程序员私活渠道揭,探索项目接单秘

对于程序员来说&#xff0c;接私活是很正常的事情&#xff0c;在工作闲暇时间利用业余时间来赚点零花钱还是非常不错的&#xff0c;但是如果能一边接私活一边提高自己的水平那就更好了。 这里给大家的建议就是&#xff0c;可以接一些稍微有难度但是在自己能力范围的项目&#x…

自动化测试:封装入口文件

1自动生成测试报告&#xff0c;start_dir是要找入口文件的相对目录。测试用例有规律的话&#xff0c;pattern可以批量执行&#xff08;通过相同的开头*&#xff09; start_dir 是要执行的测试文件所在的目录&#xff0c;pattern可以理解为具体的执行测试的文件 2所有的底层操…