【答疑解惑】C/C++参数传递

有群友问如下一个问题,他说在下图中sun函数内部的打印是对的,但是为什么调用结束之后主调的结果确是错误的。也就是说,函数sun为什么不能把相加的结果带回主调函数?

要正确理解这个问题,就要了解C/C++语言的参数传递,以及变量在内存中的存储这两个问题,这两个问题其实也是初学者在C/C++学习很容易出现的问题。

C/C++的参数传递

C/C++规定参数传递用于都是传递数值,而不会把参数的地址传递给子函数。注意不要把指针作为参数混为一谈,以为指针就是传递了参数的地址!本质上指针也是传递的一个数值,初学者一定要理解这点。比如以下两个代码片段:

int fun(int n, int *pn)

{

}

int main(void)

{

int a = 5;

int b;

fun(a, &b);

}

在函数fun传递a和&b时,是将a的数字(5)和b的地址这个数字传给子函数,虽然在这里第二个参数的意义是一个变量的地址没错,但是在传给fun时,它只认为他是一个数,不会因为加了&符号就把它认为是一个变量的地址。比如b的地址为0x00401234,也就是&b为0x00401234,但是在汇编层面的参数传递(可能放到寄存器中,也可能放到某个内存地址),它只管把0x00401234这个数值传给子函数,不会关心这个数本身是一个地址,还是地址的地址,甚至地址的n次方地址^_^,所以说C/C++传递的永远都是值传递。至于在子函数里面你想怎么理解这个数,那是你在编写子函数的时候决定的,所以C/C++中你可以把指针作为整数进行运算,这也是C/C++灵活的一个方面。

变量在内存中的存储

这个问题网上有大量的文章,程序员互动联盟网站(http://www.coderonline.net)以及以往的文章中也出现过,这个只是强调一下在子函数中申请的变量(包括形参变量)都是在调用子函数是有栈指针ESP直接移动产生的。也就是说当需要一个栈中的变量时,栈指针就一定一个位置,于是留下一个32位的内存空间就作为这个变量的存储空间。这个过程在所有的函数中都一样,也包括主函数。这里需要强调的是每个函数在调用时都有一个栈帧(基地址就是BSP寄存器决定)。所以实际上每一个函数内部的变量内存单元都可能跟其他不一样,因为他们的栈帧都很可能不同,位置也就不同了。至于什么静态变量、全局变量等等就不在这里讨论,因为跟今天的这个问题没有什么关系。

OK,现在两个概念都基本说清楚了,我们来分一下这个问题是怎么出现的。

首先,主函数分配了三个变量c、d、f (a,b,sum作为指针变量在这里意义不大,可以直接传变量的地址),并在调用子函数时把三个变量c、d、f的地址作为数值传递给sun这个函数。根据上面的分析,c、d、f这三个变量的内存单元是在主函数main这个栈帧里面存在。在子函数处理时,定义了一个变量s,这个s在子函数的栈帧中,用于接收形参a和b这个数值分别作为指针所指向的内存单元的值的和。到这里为止,一切都很正常。但是接下来的一句sum = &s这个语句把一切都搞砸了。我们看看sum是主函数传递过来的f的地址这个数值,假设这个数值是0x00401234,但是这里我们看到它把sum重新改写为s的地址,假设为0x00405678,原本sum的数值是一个位于主函数栈帧中的存储单元f的地址,也是希望向这个地址中0x00401234写入结果,自然就能再主函数中访问的,但是现在换成了一个子函数sun栈帧中的一个变量的地址0x00405678。所以在子函数中打印这个sum所指向的地址的值(s)是没有问题的,但子函数已把f的地址偷梁换柱了,执行完后,主函数的f的地址所在的单元0x00401234根本没有被赋过值,所以主函数的f原来是什么还是什么。

理解之后,要正确解决这个问题就是不要覆盖sum这个数,并且把加的结果放到sum这个里面即可,比如*sum = s。

总结起来就是,本来要用sum这个包接收物品,但是子函数却另外拿了一个包替换了这个包,并且往新的包里放了,原来那个包的所有者把包收回去后,当然在包里面没有想要的物品。但是要理解整个过程,需要理解上面的两个方面的C/C++知识。

原文发布于微信公众号 - 程序员互动联盟(coder_online)

原文发表时间:2015-09-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏hbbliyong

C++为啥要使用new

1.为什么要有new? 为什么要有new?为什么要动态创建对象?为什么有时候不用new,有时候又用new,比如: // Cocos2d-x3.x的Value类,...

43612
来自专栏三丰SanFeng

Linux64位程序移植

1 概述 Linux下的程序大多充当服务器的角色,在这种情况下,随着负载量和功能的增加,服务器所使用内存必然也随之增加,然而32位系统固有的4GB虚拟地址空间限...

2807
来自专栏IT派

Python字节码介绍

如果你曾经编写亦或只是使用Python语言,那么你可能已经习惯了看Python源码文件; 源码的文件名以.py结尾。或许你也已经注意到了另一种类型的文件,文件名...

2163
来自专栏算法channel

面试被问到动态内存分配时需要注意哪些坑,该怎么回答?

面试时,面试官问我们Java,Python这种语言那是必须要准确回答的,很多系统如果对性能要求高的话,底层一般会用到C/C++语言,因此被问到底层语言的相关知识...

1463
来自专栏抠抠空间

Python基础(一)

一、Python的简介 1、Python的由来与版本 1.1 python的由来 python的创始人为吉多·范罗苏姆(Guido van Rossum)。1...

4199
来自专栏安恒网络空间安全讲武堂

堆栈基础(一)

1566
来自专栏用户2442861的专栏

JSON 入门指南(IBM)

尽管有许多宣传关于 XML 如何拥有跨平台,跨语言的优势,然而,除非应用于 Web Services,否则,在普通的 Web 应用中,开发者经常为 XML 的...

1051
来自专栏Python绿色通道

Python入门三部曲(三)

在函数greet_user()中,变量username是一个形参—-函数完成其工作所需要的一项信息.在代码greet_user(‘kobe’)中,值’kobe’...

1453
来自专栏Java成长之路

三、JVM之对象的创建

上篇博文中已经介绍过了jvm内存的概况,接下来我们从jvm的角度来重新来认识一下Java对象是如何创建。 Java是一门面向对象的语言,在Java程序运行的...

952
来自专栏Java面试通关手册

深入理解原型模式 ——通过复制生成实例

Java面试通关手册(Java学习指南,欢迎Star,会一直完善下去,欢迎建议和指导):https://github.com/Snailclimb/Java_G...

1863

扫码关注云+社区

领取腾讯云代金券