【答疑解惑】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 条评论
登录 后参与评论

相关文章

来自专栏专注 Java 基础分享

漫谈计算机编码

我们知道,在计算机内部,所有的信息都是以二进制形式进行存储。无论是字符,或是视频音频文件,最终都会对应到一串由 0 和 1 构成的数字串。所以从我们能看懂的人类...

3396
来自专栏编程

Python之路-day6

所谓高阶函数,简单点说就是将一个函数作为另一个函数的传入参数,这样我们就称这个组合函数为高阶函数。 举个例子: map()函数能接收两个参数,一个为函数,一个为...

1718
来自专栏落影的专栏

程序员进阶之算法练习(三十三)LeetCode专场

BAT常见的算法面试题解析: 程序员算法基础——动态规划 程序员算法基础——贪心算法 工作闲暇也会有在线分享,算法基础教程----腾讯课堂地址。 今天继续Lee...

791
来自专栏用户2442861的专栏

字符编码笔记:ASCII,Unicode和UTF-8

今天中午,我突然想搞清楚Unicode和UTF-8之间的关系,于是就开始在网上查资料。

411
来自专栏take time, save time

可能是最通俗的Lempel-Ziv-Welch (LZW)无损压缩算法详述

  最近工作正好接触到这一块,试着自己总结了一下,给需要的人提供一点帮助。 一、概述      首先看看百度百科里的一句话介绍:“LZW就是通过建立一个字符串表...

6607
来自专栏猿人谷

进制之间的转换

今天翻了一本计算机基础的书籍,其中十进制、二进制、八进制、十六进制之间的转换挺有意思的,也容易犯糊涂,特温故而知新。 十进制数制系统 十进制数制系统包括 1...

18410
来自专栏有趣的Python

Python零基础入门看完这一篇就够了: 零基础入门笔记Python开发环境搭建Python的初次体验Python变量和数据类型Python集合类型:list和tuplePython的条件判断和循环

学习python有一年多了,希望通过这篇两万字的学习笔记来复习了,也能让后来者少走一点弯路。在慕课网课程笔记的同时加入了一部分自己的经验补充。 [√] 慕课网P...

4738
来自专栏C语言C++游戏编程

这是C语言无法修改得东西,C语言基础教程之常量解析

常量可以是任何基本数据类型,如整数常量,浮点常量,字符常量或字符串文字,还有枚举常量。

541
来自专栏aCloudDeveloper

公司数据结构+算法面试100题

1.把二元查找树转变成排序的双向链表(树) 题目: 输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。 要求不能创建任何新的结点,只调整指针的指向。 ...

3729
来自专栏HTML5学堂

揭开身份证验证的神秘面纱

正则验证身份证号码 HTML5学堂:曾经一直觉得用正则验证身份证号码是很简单的~但是,当真正挖掘身份证号码的规则之后,才发现,想要写好一个正则验证也没有那么容易...

3575

扫描关注云+社区