接前面二篇,继续开始新的研究

前面忘了说什么是主调函数与被调函数

主调函数:执行调用其它函数语句所在的函数

被调函数:被其它函数所调用的函数

简单说就是一个是发起调用者,另一个是被调用者

写个小例子说明下,一看就懂

Main函数就是主调函数,test_A()这句语句所在的函数就是主调函数

tset_A就是被调函数, 它是被主调函数Main中的语句test_A()进行调用的

重归正题

参数传递分为2类

1.普通传递(形参数据类型前面没有ref或者out关键字,传递的是变量中的数据)

2.引用传递(形参数据类型前面加上ref或者out关键字,传递的是变量在栈中的地址)

普通传递根据参数的数据类型分为普通传递值类型跟普通传递引用类型

普通传递值类型

小例子如下:

主调函数Main部份

1.定义二个变量a与b

2.主调函数里调用MyAdd,把变量a与b当作实参进行传递

被调函数MyAdd部份

1.把参数a与参数b进行相加,保存在临时变量c中

2.返回前对a与b进行修改,然后返回变量c中的二个数相加的结果

最终返回到Main函数中,变量a与b的值没有发生改变

总结下:

普通传递值类型,传递实参后被调用函数内部对它进行修改不会影响到主调函数中的变量

因为变量是值类型,变量中的数据是数值

普通传递引用类型

小例子如下:

主调函数Main部份

1.主调函数main中先实例化一个自定义的类TestClass,然后保存在引用类型变量temp中

2.调用被调函数test_A, 把变量temp当作实参进行传递

被调函数tset_A部份

1.把参数TestClass类型的对象temp中的MyName属性修改为大白腿

最终返回到Main函数中,引用类型变量temp中的Myname字段发生改变

总结下:

普通传递引用类型,传递实参后被调用函数内部对它进行修改会影响到主调函数中的变量

因为变量是引用类型,变量中的数据是引用地址,引用地址是对象在托管堆中的内存地址

当外部对引用类型变量进行修改时,相当于直接对托管堆中的数据进行了修改.

引用传递(形参数据类型前面加上ref或者out关键字,传递的是变量在栈中的地址)

引用传递的主要目的是对参数进行修改,然后让外部数据进行同步更新,返回多个数据的作用

写到这估计又有人有疑问了,直接在函数前面定义返回值用来接收返回值不行吗?

一来麻烦,因为如果需要对返回值进行接收,同步更新变量,那接收返回值还得进行赋值操作

二来返回值只能返回一个数据,如果函数有多个形参,需要对多个参数进修改,让外部数据进行同步更新,

起到返回多个数据的目的,那就只能使用引用传递的方式。

引用传递根据参数的数据类型分为引用传递值类型跟引用传递引用类型

根据前面说的普通传递,那引用类型的变量需要引用传递吗?大多数情况下不需要,因为前面说过,

引用类型的实参,传递的是引用地址,其它地方对它进行修改,会直接对堆中数据进行更改

真正常用的是多个值类型的实参,才需要使用引用传递的方式

写了一大堆引用传递,还是写个值类型的引用传递来看下效果

引用传递值类型 (out)

小例子如下:

主调函数Main部份

1.定义四个变量a,b,addNum,maxNum

2.调用Test函数,前二个实参a,b只是用来计算的,并不需要返回数据或者修改数据,所以不使用引用传递.

后二个参数是用来接收返回的二个数相加的结果,所以需要使用引用传递参数,这里使用的是out方式

被调函数

1. 对二个数进行相加,然后根据传递过来的addNum在主函数main中的变量地址,对这个地址写入新的数据

2. 对二个数进行比较,然后根据传递过来的maxNum在主函数main中的变量地址,对这个地址写入新的数据

从这里就能看出使用引用传递的好处了

顺带验证Test函数中addNum下到底是不是主函数main中的变量地址呢?

在Test中的addNum = a + b; 下个断点,添加临视,看下addNum中是什么

从图中看,addNum是值啊,不是地址…估计到这有人迷茫了.

其实这是vs特意隐藏了内部的细节,这里的值是是addNum在主函数main中的变量地址下的值,

并不是真正addNum下的数据…

为了证明这点,在主调函数的Test(a,b, out addNum, out maxNum); 这一行再下个断点

F5运行调试,此时断下来,我们转到反汇编,好好分析下它是怎么传递参数的

这七行反汇编代码对应的就是Test(a,b, out addNum, out maxNum);

第一句Lea eax,[ebp - 48h]    //ebp-48h 是变量addNum在栈中的地址,Lea是传地址操作,相当于mov eax,ebp-0x48

第二句push eax   //看过前二篇的就知道这是因为参数多于2个,使用压栈的方式传递参数,这里push的是addNum在栈中的地址

第三句Lea  eax,[ebp - 4ch]   //ebp-4ch 是变量maxNum在栈中的地址,Lea是传地址操作,相当于mov eax,ebp-0x4c

第四句push eax //看过前二篇的就知道这是因为参数多于2个,使用压栈的方式传递参数,这里push的是maxNum在栈中的地址

第五句mov ecx,dowrd ptr [ebp-40h] //这是传递的临时变量a

第六句mov edx,dowrd ptr [ebp-44h] //这是传递的临时变量b

第七句 call 00360c30   //通过间接调用的方式调用Test函数

这里可以单步执行,纪录下ebp-0x48 与ebp-0x4c的值

addNum = 071CE950
maxNum = 071CE94C

进入到Test函数内部, 先单步执行完前二句,查看下堆栈中的情况

[Ebp] = 0x071CE928 = 当前ESP栈顶

[EBP + 4] = 执行完当前函数后的返回地址

[EBP + 8 ] = 传递过来的第四个实参maxNum

[EBP + 0xC] = 传递过来的第三个实参addNum

这里的参数顺序是因为主调函数中是从左向右往堆栈中压入参数三跟参数四,但是在堆栈中栈的增长是从内存地址的高位往

内存地址的低位进行增长的,每压入一个数据,esp - 4开辟四个字节空间,然后esp指针指向esp-4的位置

后面的一些不需要关心了,转回源代码,直接在addNum = a + b; 这一行下个断点

F5运行调试,转到反汇编继续看

这四行反汇编代码对应的就是addNum = a + b;

第一行mov eax,dword ptr [ebp - 3ch]; //取得第一个实参a,赋值给寄存器eax

第二行add eax,dowrd ptr [ebp - 40h]; //取得第二个实参b,把寄存器eax与b进行相加,再赋值给eax

第三行mov edx,word ptr[ebp + 0ch]; //取得堆栈中的addNum的变量地址

第四行mov dword ptr [edx],eax  //把二个数相加的结果写入到addNum的变量地址下

其实这里的引用传递的实参就是C++中的一个指针变量,不过C#没有指针这玩意就没法讲

确定了引用传递参数是传递地址,继续回来,使用ref来写个小例子

引用传递值类型 (ref)

小例子如下:

总结下:

ref跟out都是引用传递参数, 传递的是实参在主调函数的变量地址,在被调函数中参数是个指针变量,

参数指向了主调函数中的变量地址, 对参数进行操作,

实际就是对指针所指向的主调函数中的变量地址进行了读取或者写入数据

ref 跟 out的区别

ref 传递的实参在主调函数部份必须对它进行赋值,调用函数时实参前面加上ref关键字

在被调函数部份可以直接使用实参,不需要在被调函数里进行赋值

out 传递的实参在主调函数部份不需要对它进行赋值,调用函数时实参前面上out关键字

在被调函数部份不能直接使用实参,需要在被调函数里先赋值,才能进行访问

还有更复杂的引用传递引用类型,很少能用到,多余了,大多还是面试题会问.

C#中的函数(三)参数传递及返回值的更多相关文章

  1. JavaScript学习系列博客_17_JavaScript中的函数的参数、返回值

    数的形参(形式参数) - 定义函数时,可以在()中定义一个或多个形参,形参之间使用英文逗号隔开:定义形参就相当于在函数内声明了对应的变量但是并不赋值,形参会在调用时才赋值. 函数的实参(实际参数) - ...

  2. perl 函数参数传递与返回值(一)

    perl 函数参数传递与返回值(一) http://www.cnblogs.com/tobecrazy/archive/2013/06/11/3131887.html

  3. 【Go入门教程3】流程(if、goto、for、switch)和函数(多个返回值、变参、传值与传指针、defer、函数作为值/类型、Panic和Recover、main函数和init函数、import)

    这小节我们要介绍Go里面的流程控制以及函数操作. 流程控制 流程控制在编程语言中是最伟大的发明了,因为有了它,你可以通过很简单的流程描述来表达很复杂的逻辑.Go中流程控制分三大类:条件判断,循环控制和 ...

  4. 【Go入门教程5】流程(if、goto、for、switch)和函数(多个返回值、变参、传值与传指针、defer、函数作为值/类型、Panic和Recover、main函数和init函数、import)

    这小节我们要介绍Go里面的流程控制以及函数操作. 流程控制 流程控制在编程语言中是最伟大的发明了,因为有了它,你可以通过很简单的流程描述来表达很复杂的逻辑.Go中流程控制分三大类:条件判断,循环控制和 ...

  5. 总结day7 ---- 函数的内容 ,初识,返回值,进阶(一)

    内容大纲: 一: 函数识别 二: 函数的结构 三: 函数的返回值, 四: 函数的参数 五: 动态参数 六: 形参的顺序 七: 名称空间 八: 作用域 九: 加载顺序和取值顺序 十: 内置函数 十一: ...

  6. Python——变量的引用和函数的参数和返回值的传递方式

    变量的引用 在python中,所有的变量都是指向地址,变量本身不保存数据,而是保存数据在内存中的地址.我们用下面的程序来理解: a = 10 print(id(a)) a = 11 print(id( ...

  7. c&c++函数的参数和返回值的传递终结版

    c++函数的参数和返回值的传递方式有三种:值传递.指针传递和引用传递. 在这之前先看几个例子: 一, int a=10; int b=a; b+=10; 此时b是a的一个拷贝,改变b的值,a并不会受到 ...

  8. JAVA中执行JavaScript代码并获取返回值

    JAVA中执行JavaScript代码并获取返回值 场景描述 实现思路 技术要点 代码实现 测试方法 运行结果 改进空间 场景描述 今天在CSDN上偶然看到一个帖子对于一段字符串 “var p=‘xx ...

  9. 探寻main函数的“标准”写法,以及获取main函数的参数、返回值

    main函数表示法        很多同学在初学C或者C++时,都见过各种各样的main函数表示法: main(){/*...*/} void main(){/*...*/} int main(){/ ...

随机推荐

  1. 认识map-reduce

    基本概念 map-reduce1.0 例子: hadoop streaming 用语言驱动map-reduce的话,使用的hadoop streaming命令,可以通过python,php,java来 ...

  2. long 在不同操作系统下所占用的字节数

    不同平台下C\C++数值数据类型长度如下: 类型 win32 win64 linux32 linux64 其中long类型和指针类型需要特别注意,编写跨平台的软件时尽量不要使用long类型,或者需要对 ...

  3. gcc/g++ -O 优化选项说明

    查查gcc手册就知道了,每个编译选项都控制着不同的优化选项 下面从网络上copy过来的,真要用到这些还是推荐查阅手册 -O设置一共有五种:-O0.-O1.-O2.-O3和-Os. 除了-O0以外,每一 ...

  4. R数据挖掘 第三篇:聚类的评估(簇数确定和轮廓系数)和可视化

    在实际的聚类应用中,通常使用k-均值和k-中心化算法来进行聚类分析,这两种算法都需要输入簇数,为了保证聚类的质量,应该首先确定最佳的簇数,并使用轮廓系数来评估聚类的结果. 一,k-均值法确定最佳的簇数 ...

  5. STM32 串口通信基础知识

    1.串口通信简介 通信接口的两种方式: 并行通信 -传输原理:数据各个位同时传输. -优点:速度快 -缺点:占用引脚资源多 串行通信 -传输原理:数据按位顺序传输. -优点:占用引脚资源少 -缺点:速 ...

  6. WPF 精修篇 管理资源字典

    原文:WPF 精修篇 管理资源字典 样式太多  每个界面可能需要全局的样式 有没有肯能 WPF 中的样式 像Asp.net中 的CSS一样管理那 有的 有资源字典 BurshDictionary &l ...

  7. vue基础之data

    使用 调用data onLoad(option) { _self = this; _self.$data.xxxx = "te"; } 绑定节点 元素~~~~ <input ...

  8. 在<a></a>标签中如何调用javaScript脚本

    在日常工作总会遇到在<a>标签中执行js代码的情况 现在做一个总结,希望对大家有一个帮助. 1.a href="javascript:js_method();" 这是我 ...

  9. [個人紀錄] git 疑難排解

    1.git pull 時發生錯誤,要拉下來的檔案內含有檔名太長的檔案 ans: https://stackoverflow.com/questions/22575662/filename-too-lo ...

  10. Visual Studio Code创建C#项目

    Visual Studio Code是一个支持跨平台的文本编辑器,同其他文本文本编辑器一样,不但占用磁盘空间小,性能也比较快:近几年由于不断的升级和许多开发者提供大量的插件,它已经成为了一个非常强大的 ...