函数知识总结

  • 概括
    C++程序由函数构成,函数之间通过传递参数和返回值进行通信。C++支持按值和按引用传递参数。C++允许函数重载,即几个不同的函数可以使用相同的名字,编译器会自己选择与函数调用相匹配的版本。函数可以定义自己的数据,这些数据不在整个程序中使用,可以将其限制在一定的访问范围内。作用域提供了一种机制,让程序员能够限制程序中声明的各种名字的可见性。还要了解各种作用域以及对象的存储方式。

  • 函数基础

  • 函数定义
    语法形式
    返回类型 函数名 (参数列表) {函数体}
    实例:
    int fact (int n) //函数名fact ,返回类型int ,一个int类型的形参n
    {
    int ret=1;
    while(n>1)//求阶乘
    ret *=n–;
    return ret;//返回结果
    }
    如果函数没有任何参数,空用空参表或void参数表表示,int foo();与int foo (void);等价。
    参数列表由逗号分隔的形参类型列表构成,每个形参类型之后跟一个可选的形参名字,例:
    int print (int value,int base) {…} int exchange (int v1,int v2) {…}
    如果参数没有名字则不能在函数体中使用。

  • 函数调用
    当函数名后紧跟着操作符“()”时,该函数就被调用执行。如果函数定义要求接受参数,调用函数时就需要为这些参数提供相应的数据,形式为:函数名(实参列表):
    例: int main (){
    int val = fact (5);调用fact()函数,实参为5,结果保留在val中
    cout<<“5!=”<<val<<endl;
    return 0;
    }
    C++程序编译器在编译时会对每个函数调用的实参执行参数类型检查。若实参类型与相应的形参类型不匹配,如果有可能,就会应用一个隐式的类型转换;如果不可能进行隐式类型转化或实参的个数不正确,就会产生一个编译错误。
    例:double类型能转化为int类型,int类型实参可以转化为形参const int类型
    实参const int* 类型不能转化为int*形参类型。
    函数调用时主要完成两项工作:一是用实参初始化函数对应的形参,二是将控制权转移给被调用的函数。当遇到return语句时,结束函数的执行。return语句有两个作用:一是返回return语句的值,二是将控制权从被调用函数转移回主调函数。函数的返回值用于初始化调用表达式的结果,之后继续完成调用表达式的剩余部分。

  • 函数声明
    函数在使用前必须声明。一个函数可以在程序中多次声明,函数定义也可以被用作声明,但是函数在程序中只能定义一次。函数声明由函数返回类型、函数名和形参列表构成。在函数声明中,形参的名字不是必须的,函数声明中的形参名可以和函数定义中的形参名不同,有意义的参数名可以提高函数的可读性。为避免同一函数多次声明不一致,一般将函数声明放在头文件中

  • 递归函数
    直接或间接调用自己的函数称为递归函数。递归函数必须定义一个停止条件,否则会陷入无限递归调用中,通常方法是将递归函数放在if语句中。

  • 参数传递
    每次调用函数时,会创建形参变量,并用传入的实参初始化形参。形参的类型决定了实参初始化形参的方式如果形参是引用类型,那么它将绑定到对应的实参上,否则,将实参的值复制后赋给形参。
    当形参是引用类型时,对应的实参被称为按引用传递,或传引用调用函数。此时,引用形参绑定到实参,是实参对象的别名。当实参的值被复制给形参时,形参和实参是两个独立的对象,实参被称为按值传递,或传值调用函数。
    按值传递
    按值传递参数和函数调用过程。

#include<iostream>
using namespace std;
int test (int left,int right){
 return left+right;
 }
 int main(){
  int lval=2;
  int rval=3;
  int result=test(lval,rval);
  }

调用test函数,将主函数挂起;为test分配一块栈空间,并进行参数传递;将实参rval和lval的值先后压栈,分别初始化right和left。在大多数编译器中处理函数调用时对参数按照从右到左的次序进行压栈处理;如先将rval入栈——相当于为right分配空间并将rval的值复制到right,再将lval入栈。
执行test函数,将left的值与right的值相加,结果存放存储函数返回值的外部单元中(此处到寄存器中)。
test函数执行结束,test栈的空间被收回,left和right的空间被撤销;函数返回,恢复主函数的执行。
为result分配4个字节的存储空间;并将test的返回值即寄存器的值5放入其中。
main()的执行即将结束,做些清理工作(如清空寄存器),程序结束。
注:1、按值传递参数并不会改变实参的内容,如果要修改实参的值时,按值传递无法做到。2、当大型的类对象或结构体变量作为参数按值传递时,为对象在运行栈中分配空间并复制对象的时间和空间开销往往较大。
解决方案一:将参数声明为指针用变量的地址实施调用。二:将参数声明为引用。

将参数声明为指针用变量的地址实施调用实例:

#include<iostream>
using namespace std;
void pswap (int *pv1,int *pv2){
//交换pv1、pv2指向的值的函数
int t;
t=*pv1;
*pv1=*pv2;
*pv2=t;
}
int main(){
int ival1=10;
int ival2=20;
int *p1=&ival1;
int *p2=&ival2;
cout<<ival1<<" "<<ival2<<endl;
pswap(p1,p2);
cout<<ivall<<" "<<ival2<<endl;

原理
执行int t;为t分配4个字节的内存。执行t=*pv1;通过pv1中存储的地址间接访问ival1

按引用传递
函数操纵的形参是实参的别名(在大多数编译器实现中实际上和实参占据相同的内存空间,因而可以改变实参的值。
实例:

#include<iostream>
using namespace std;
void rswap(int& rv1,int& rv2)
{
  int t;
  t=rv1;
  rv1=rv2;
  rv2=t;
  }
  int main()
  {
   int ival1=10;
   int ival2=20;
   cout<<ival1<<" "<<ival2<<endl;
   rswap(ival1,ival2);//相当于int&rv1=ival1
   cout<<ival1<<" "<<ival2<<endl;

引用参数一般有三种用途。第一种通过传递引用在函数内修改实参的值。引用参数的第二种用途就是向主调函数返回额外的结果。一个函数最多只能返回一个参数,如果希望多得到一个结果,就需要通过函数的实参带回额外的值。第三种用途向函数传递一个大型的结构体变量或类对象。例:

struct Huge{
 int stuff[1000];
 };
 void foo(Huge & h){....}
// 调用foo()时,不需要复制实参对象。
// 担心传递引用会实参函数被修改,那么可以将参数声明为const引用
void foo(const Huge& h) {.....}//函数体内修改h的代码会引起编译错误

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值