函数传参数组时,使用数组形参的本质

c语言中函数的调用分为两种方式:传址调用、传值调用。

传值调用我们都知道就是将实参的值传送给被调函数,让被调函数的形参接收这个值,从而形参内存中的数据就变成了实参的一份拷贝。

而传址调用则是将实参的地址传送过去,然后令实参为一指针变量,这个指针变量接收实参的地址,从而使这个指针变量内保存的数据变成实参的地址。

传值调用的功能更加强大,因为它是一个指针,可以通过解引用直接访问到实参。

但是,随着我了解了函数栈帧的开辟以及形参的压栈,我发现了一个问题:

就是这个函数

 

请问,这个函数里面的形参到底是个什么东西,它真的像它的表面一样,是个数组形参吗? ArrInit函数栈帧空间进行开辟时,压栈的是什么呢?

数组是如何进行传参的?在学校中,我们的老师给我们讲的是:我们可以理解为因为数组名等于首地址,将数组名传送过去之后,形参数组名接受到这个地址,那么两者就共用同一块空间。

是的,老师为了让我们不太混乱,给了我们一个挺好的理解。但是这个理解我现在有点感觉不太对了。首先,数组名是首地址,是一个常量,一个指针常量。它不是变量!这个很重要,数组名一般情况下都是数组的首地址,是一个常量,不能作为一个变量来使用。只有两种情况下数组不是数组的首地址:

第一:使用sizeof计算数组大小时,此时数组名不是首地址,数组名此时就是一个抽象的变量名,类似于int a = 10 中的a这个变量名, 而它所代表的空间就是数组一整块空间。所以sizeof求出来的就是这一整块空间的大小。

第二:&数组名。 &是取地址符号, 取的是一个对象的地址。那么当&加数组名时,取出来的就是一个数组的地址。数组的地址,是一种指针类型,需要用数组指针接收(数组指针,一种情况就是用在此场景,另一种常见场景就是二维数组的传参)

除了以上两种情况,其他情况下数组名都作为首地址进行处理。

带着这个结论,我们再看上面的两个问题

既然数组名等于函数首地址,那么函数调用传参传过去的必定是一个地址。也就是传址调用。那么接收这个地址的必定是一个指针。( 数组名是一个常量,不可能用来接收,所以形参处的数组名不可能与实参的数组名代表同一地址。) 既然是传址调用,那么形参一定是个指针。

所以,我们不妨假设, int farr【10】这里其实不是一个数组,而是一个指针。那么如何证明我们的假设是否成立,我们只要使用sizeof。 使用sizeof进行计算farr这个对象的大小时,假如我们的假设成立,那么sizeof计算出来的就是4 / 8(取决于你的环境)。假如我们的假设不成立, 它如果还是个数组,那么farr身为数组名,使用sizeof进行计算,就一定是40.

现在我们来看一下结果:

结果是8, 我们的假设成立。证明farr确实是一个指针变量。至于为什么形参写成int farr[10]的形式,结果还是一个指针,我认为可能是因为这样:

我们都知道*(arr + i) = arr[i];这里我认为同样可以这样理解,farr【10】其实只是一个指针解引用的形式。而且其实farr【10】里面的这个10,在这里无论是任何数,它都没有语法问题。这其实就说明这个10是一个没有意义的数字。要知道,函数调用的形参是一份拷贝,是要进行压栈的,拷贝的数据越大,占用内存也就越大。传送数组函数进行接收时,必定要创建一个形参,假如创建一个相同的数组,这个空间就占用太大了,而如果是一个指针的话,空间花费就少了太多。而运用方式相差不大。从这个方面考虑,就大概可以理解为什么这个数组的形参默认被降维成为一个指针了。

所以,我对于我之前的疑问就有了一个自己的答案,函数的数组形参其实并不是一个数组。形参因为压栈要进行拷贝占用空间,出于节省的考虑。将形参降维成了一个指针。而压栈的必定也不会是整个数组,本质上只是一个指向实参数组首地址的指针

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

openGauss学习笔记-204 openGauss 数据库运维-常见故障定位案例-重建索引失败

文章目录 openGauss学习笔记-204 openGauss 数据库运维-常见故障定位案例-重建索引失败204.1 重建索引失败204.1.1 问题现象204.1.2 原因分析204.1.3 处理办法 openGauss学习笔记-204 openGauss 数据库运维-常见故障定位案例-重建索引失败 204.1 重建索引失败 204.1.1 问题现…

HarmonyOS SDK,助力开发者打造焕然一新的鸿蒙原生应用

鸿蒙生态千帆启航仪式于1月18日正式启动。从2019年HarmonyOS正式发布到2020年“没有人能够熄灭漫天星光”,今天,满天星光终汇成璀璨星河,HarmonyOS NEXT鸿蒙星河版重磅发布,带来了全新架构、全新体验、全新生态。作为支撑鸿蒙原生…

Ubuntu使用docker-compose安装redis

ubuntu环境搭建专栏🔗点击跳转 Ubuntu系统环境搭建(十三)——使用docker-compose安装redis 文章目录 Ubuntu系统环境搭建(十三)——使用docker-compose安装redis1.搭建文件夹2.docker-compose.yaml配置文件3.redis.co…

Flutter轮播图Banner

使用插件:flutter_swiper 实现轮播图 pubspec.yaml 增加 :flutter_swiper : ^lastest_version 在项目文件夹下打开命令行执行:flutter packages get 安装插件 home_page.dart中使用swiper 程序运行:先启动虚拟设备后,执行命令f…

【git分支管理策略】

文章目录 前言一、分支管理策略简介二、git基本操作三、git分支远程分支本地分支 四、gitflow分支管理策略分支定义gitflow分支管理策略评价 五、GITHUB FLOW分支管理策略分支使用流程创建分支(Create a branch)新增提交(add and commit)提出 Pull 请求&…

重拾计网-第四弹 计算机网络性能指标

ps:本文章的图片内容来源都是来自于湖科大教书匠的视频,声明:仅供自己复习,里面加上了自己的理解 这里附上视频链接地址:1.5 计算机网络的性能指标(1)_哔哩哔哩_bilibili ​​​ 目录 &#x…

《WebKit 技术内幕》学习之五(3): HTML解释器和DOM 模型

3 DOM的事件机制 基于 WebKit 的浏览器事件处理过程:首先检测事件发生处的元素有无监听者,如果网页的相关节点注册了事件的监听者则浏览器会将事件派发给 WebKit 内核来处理。另外浏览器可能也需要处理这样的事件(浏览器对于有些事件必须响应…

一零七七、将Hexo cl Hexo g Hexo s通过systemctl命令管理

背景: 服务器需要执行hexo s来运行项目,但这个命令是基于前台的,故想直接嫁接在systemctl命令基础上来控制环境: Centos 8 前置环境就不说了,Hexo安装好,起码装完自己得先看hexo命令生效没,前置环境做好后…

【MATLAB】ICEEMDAN+FFT+HHT组合算法

代码基本原理 ICEEMDAN(改进的完全经验模态分解与自适应噪声)FFT(快速傅里叶变换)HHT(希尔伯特-黄变换)组合算法是一种用于信号处理和分析的复杂组合算法。它结合了ICEEMDAN、FFT和HHT三个步骤&#xff0c…

《WebKit 技术内幕》学习之十(2): 插件与JavaScript扩展

2 Chromium PPAPI插件 2.1 原理 插件其实是一种统称,表示一些动态库,这些动态库根据定义的一些标准接口可以跟浏览器进行交互,至于这个标准接口是什么都可以,重要的是大家都遵循它们,NPAPI接口标准只是其中的一种&a…

11- OpenCV:自定义线性滤波(卷积,卷积边缘)

目录 一、卷积 1、卷积概念 2、卷积如何工作 3、常见算子(卷积核 Kenel) 4、自定义卷积模糊 5、代码演示 二、卷积边缘 1、卷积边缘问题 2、处理边缘 3、相关的API说明 4、代码演示 一、卷积 1、卷积概念 (1)在OpenC…

Elasticsearch基础篇(八):常用查询以及使用Java Api Client进行检索

ES常用查询以及使用Java Api Client进行检索 1. 检索需求 参照豆瓣阅读的列表页面 需求: 检索词需要在数据库中的题名、作者和摘要字段进行检索并进行高亮标红返回的检索结果需要根据综合、热度最高、最近更新、销量最高、好评最多进行排序分页数量为10&#xf…