第18章 更入的理解——函数进阶.pptVIP

  1. 1、本文档共25页,可阅读全部内容。
  2. 2、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
  3. 3、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载
  4. 4、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
  5. 5、该文档为VIP文档,如果想要下载,成为VIP会员后,下载免费。
  6. 6、成为VIP后,下载本文档将扣除1次下载权益。下载后,不支持退款、换文档。如有疑问请联系我们
  7. 7、成为VIP后,您将拥有八大权益,权益包括:VIP文档下载权益、阅读免打扰、文档格式转换、高级专利检索、专属身份标志、高级客服、多端互通、版权登记。
  8. 8、VIP文档为合作方或网友上传,每下载1次, 网站将根据用户上传文档的质量评分、类型等,对文档贡献者给予高额补贴、流量扶持。如果你也想贡献VIP文档。上传文档
查看更多
第18章 更入的理解——函数进阶

第18章 更深入的理解——函数进阶 第10章中已经讨论了函数的基本知识,让读者对函数有了基本的认识,本章从更深层次帮助读者理解函数。从和函数关系最密切的调用和返回入手,函数的参数传递有传值,传指针2种方式,从类型的角度上看,参数不仅仅可以是系统内建的数据类型,还可以是数组、结构等。此外,递归编程机制,函数的作用域和可见域,变量的生存期、作用域和可见域等都是本章讨论的重点。 18.1 参数传递的副本机制 如果将函数比作剧本,那形参和实参的关系相当于角色和演员的关系,函数的参数传递有传值和传地址两种方式。传值调用时,在函数内对形参的改变都不会影响实参,要想在函数内对实参进行操作,必须采用传地址调用的方式。这是形象化的理解,从本质上说,这是由参数传递的副本机制决定的。 所谓副本机制,是指copy(拷贝)的思想,不论是传值调用还是传址调用,编译器都要为每个参数制作临时副本,或称拷贝,函数体中对参数的修改都是对副本的修改,下面具体分析之。 18.1.1 传值调用的副本 传值调用的情况相对简单,不论传递的参数如何,编译器都为这些参数制作临时副本,函数体中对参数的修改都是针对副本进行的,丝毫不会影响传来的参数,试通过下述一段示例,体会传值调用的副本机制。 18.1.2 传址调用的副本机制 相比传值调用,传址调用似乎要复杂一点,但只要知道,传址调用也是通过副本机制,便能很好地理解传址调用的机理,同样从一个形象的例子入手。 18.2 函数返回值的副本机制 如果要细分,函数返回也可以认为存在传值和传址两种方式。函数返回同样也是根据副本机制来处理的,首先来回顾下函数返回的流程: 当执行到return语句时,return的值被复制到某个内存单元或寄存器中,其地址是由编译器来维护的,程序员无法直接访问该地址,也就是说,在函数执行完毕,相关现场被撤销前,返回的值被复制保存到了某个地方,编译器访问该位置即可知道函数的返回值。该位置即可看成是函数中返回值的副本。 对函数返回取地址是不合法的,即假设存在如下函数: int A(int b,int c); 不允许使用如下形式的语句: A(3,4); 18.2.1 return 局部变量为什么合法 函数返回的副本机制很好地解释了为什么return一个局部变量是合法的,来看一段简单的求和函数代码: int sum(int a,int b) /*函数定义*/ { int c=a+b; /*局部变量c*/ return c; /*返回*/ } …… int d=sum(1,2); /*函数调用*/ 来看语句“int d=sum(1,2);”,该语句先执行函数sum,sum函数执行完毕后将结果赋值给int型变量d,如果从字面上理解,是将c赋值给d,但实际上,在执行赋值操作时,由于函数sum已经执行完毕返回,函数中的局部变量c已被撤销,不存在了。实际上,在c被撤销前,函数已经为返回值c创建了副本,保存在特定的位置上,赋值操作是由该位置处的副本完成的,形象的示意如所示。 18.2.2 返回指针申请动态内存 下面来看一下如何通过返回指针在函数中动态申请内存,试比较下述与的异同: 代码? 通过返回指针传递动态内存GetMemorySunccess2 18.2.3 不要返回指向栈内存的指针 动态申请内存是在堆中完成的,而函数返回不会释放堆内存,但不要忘记,函数返回时,栈内存中的内容会被自动清除,因此,不要返回指向栈内存的指针。 请读者试着分析下述的问题所在: 18.2.4 返回指向只读存储区的指针 如果将中的GetMemory函数修改如下,会怎样? char* GetMemory(void) /*定义函数GetMemory*/ { char* p=Hello,C; /*栈内存中开辟字符串*/ return p; /*返回局部指针*/ } Hello,C作为常量字符串,位于程序的只读存储区(.rodata),此时,返回指向只读存储区的指针p并没有问题,但该指针只能用于输出,而不能用于输入改写。 18.3 函数与结构体 结构体可以看成一种数据组织方式,将很多不同类型的相关数据打包,构成一种新的类型,从这种意义上说,结构体变量完全可以当成是一种普通类型的变量来使用。结构体变量作函数参数时,也有传值和传址两种方式,函数返回亦是如此,既可以返回结构体变量,也可以返回指向非局部结构体变量的指针。 18.3.1 结构体变量的传值和传址调用 采用值传递时,在函数内将生成实参的“复制品”,如果参数多是像int,char之类的简单变量,这些变量占用的内存并不多,复制也快。但结构或共用体变量往往由多个成员变量组成,占用内存大,如果复制一份,会造成时间和空间双重浪费。采用值传递不会造成时空浪费

文档评论(0)

l215322 + 关注
实名认证
文档贡献者

该用户很懒,什么也没介绍

1亿VIP精品文档

相关文档