读书笔记:C++ Primer系列(14)—— C++函数及参数传递

一、函数

  实际项目中,要实现的功能不是仅靠一些基本语句就可以实现的,通常需要将这些大的功能进行分解,分步骤完成。例如:要实现一个学生信息管理系统, 一个学生信息管理系统至少包括学生信息的 添加查询删除修改等功能, 这些功能要求就是对该系统的初步分解, 然后以查询功能为例对其继续分解, 查询 功能可以再次分解为由以下几个功能组成的模块:

  • 获取用户查询信息;
  • 到数据库查询该学生信息;
  • 按照一定格式输出查询结果。

如果感觉该功能实现起来还是比较困难那么就继续分解, 直到可以顺利实现。这些功能分解后的实现就是依靠函数, 函数在广义上理解为具有具体功能的一个模块, 在函数实现后, 通过对函数的调用来使用这个功能。

1.函数声明

  函数声明也叫函数原型,包括返回类型、函数名、参数列表以及最后的一个分号。C++在处理函数调用之前,必须先看到该函数的声明或者定义。如果一个函数在不同源文件中被调用,那么每个源文件中都必须有声明。如果函数没有返回值,那么返回类型必须是void,如果什么都不写,默认返回类型是int。要避免不写返回类型

2.函数定义

  函数包括返回类型、函数名、形参列表、花括号括起来的函数体。如果函数返回类型是void,return语句可有可无,如果有的话不要带任何参数。如果函数返回类型不是void,那么至少要有一个return,并且return后必须带一个值。函数定义在一个程序中只能出现一次,但是函数原型可以有多个。

  函数定义的格式为:
返回值类型 函数名(参数列表)
{
            函数体
 }

例1: 

 int max(int a, int b)  //函数头

{//函数体

    if(a>=b)

       return a;

    else

       return b;

}

二、函数的调用

  在任何的一个C++程序中, 都有一个被称为主函数的函数 - main(),该函数又被称为C++程序的入口函数,该函数的作用是告诉程序应该从这里开始执行指令, 也就是说,,任何一个C++程序都是从 main() 函数处开始执行, 直到执行过程中遇到程序的结束指令。

  C++中的函数之间可以互相调用,即:

              main()函数可以调用任何函数,也包括自身;

              自定义的函数可以调用其他自定义的函数或者库函数,自定义函数同样也可以调用main()函数。

函数调用做了如下两件事:

  • 用对应的实参初始化函数的形参,并将控制权转移给被调用的函数;
  • 主调函数的执行被挂起,被调函数开始执行。

每次调用函数时,该函数的所有形参都会被重新创建,此时将会用实参来初始化对应的形参。

注:形参的初始化与变量初始化一样:如果形参是非引用类型,则复制实参的值;如果形参为引用类型,则它是实参的一个别名。  

1. 非引用形参

  • 非引用类型的形参是通过复制对应的实参来实现初始化(实参副本来初始化形参)。
  • 非引用类型的形参实际上是对应实参的局部副本,对非引用类型的形参的修改仅仅改变了局部副本的值,一旦函数执行结束,这些局部变量的值也就随之没有了。

例2: 

 void swap(int v1 , int v2)

{
   int temp = v2;
   v2 = v1;
   v1 = temp;   
}

 如果有函数调用swap(i, j),则i和j不受影响,只交换了副本的值。

2. 指针形参

 当函数形参是指针时,此时将复制实参指针。与非引用形参一样,该类形参的任何改变也仅作用域局部副本。如果函数将新指针值赋给形参,主调函数的实参指针的值依然不会改变。

例3:

void add(int *p)

{

     *p+=1;
       p+=1;


上例中:使用指针做为函数的形参,同样指针的值(指针的地址)不会因为p+=1而受到影响,但是指针指向的地址的值(*p)将会改变。所以要想修改实参的值,可以使用这种方法。

注意:       

复制实参并不是适合所有情况,不适合采用复制实参的情况有:

  • 当需要在函数中修改实参的值时;
  • 当需要以大型对象作为实参传递时;
  • 当没有办法实现对象的复制时。

3. 引用形参

对于例1要实现的交换两个数的功能,需要将两个形参定义为引用类型才可以,即:

 例4:

void swap(int &v1 , int &v2)

{
   int temp = v2;
   v2 = v1;
   v1 = temp;   
}

每次被调用时,引用形参被创建并与相应的实参关联。当调用swap(i, j)时,形参v1只是实参对象i的另一个名字,而v2也只是实参对象j的另一个名字,对v1或v2的任何修改,实际上就是对i或j的修改。

4.传递指向指针的引用

例5:

//修改指针指向的地址,而地址的值不变
void fun(int *&v1,int *&v2)
{
    int *p=v2;
    v2=v1;
    v1=p;
}
//交换指针指向地址的值
void fun1(int *&v1,int *&v2)
{
    int p=*v2;
    *v2=*v1;
    *v1=p;
}
int main()
{
    int i=1;
    int j=2;
    int *v1=&i;
    int *v2=&j;
    cout<<"i="<<i<<" "<<"j="<<j<<endl;//1,2
    fun(v1,v2);
    cout<<"*v1="<<*v1<<" "<<"*v2="<<*v2<<endl;//2,1
    cout<<"i="<<i<<" "<<"j="<<j<<endl;//1,2 注意这里只是指针指向的地址交换,而i和j的值没变
    fun1(v1,v2);
    cout<<"i="<<i<<" "<<"j="<<j<<endl;//2,1 这里函数交换的是指针指向地址的值
    system("PAUSE");
    return 0;
}

上例中:
int *&v1就是一个指向指针的引用,从右至左理解:v1是一个引用,与指向int类型对象的指针相关联,v1只是一个传递进函数的指针别名。 

 

原文地址:https://www.cnblogs.com/kkdd-2013/p/3714797.html