从零开始:Qt可视化程序设计基础教程
上QQ阅读APP看书,第一时间看更新

1.8 参数和函数

参数也叫参变量,它就是一个变量,传递参数就是传递变量。关于参数,可以这样去理解:魔法师可以根据观众的要求变出鸡、鸭、猫等动物,当观众要求魔法师变出鸡时,观众说出的“鸡”对魔法师而言就是参数。

函数是一组一起执行一个任务的语句。每个C++程序都至少有一个函数,即主函数 main(),所有程序都可以定义其他额外的函数。可以把代码划分到不同的函数中,如何划分代码到不同的函数中可自行决定,但在逻辑上,划分通常是根据每个函数执行一个特定的任务进行的。函数的声明告诉编译器函数的名称、返回类型和参数。函数的定义提供了函数的实际主体。

· 1.8.1 函数的定义

函数的定义一般主要由5个部分组成:返回值类型、函数名、参数列表、函数体语句、return 表达式。

具体语法如下。

    返回值类型 函数名 (参数列表)
    {
          函数体语句
          return表达式
    }

返回值类型 :一个函数可以返回一个值,有些函数只是执行所需的操作而不返回值,在这种情况下,返回值类型是void。

函数名:这是函数的实际名称,自定义函数名须满足变量名命名规范。

参数列表:使用该函数时传入的数据。

函数体语句:“{}”内的代码,函数内需要执行的语句。

return表达式:和返回值类型有关,函数执行完后,返回相应的数据。

具体定义方法见例1-29。

例1-29:定义一个加法函数,实现两个数相加。

    //函数定义
    #include <iostream>
    using namespace std;
    int add(int num1, int num2)//包括了2个形式参数(简称形参)
    {
       int sum = num1 + num2;
       return sum;//函数返回值
    }

· 1.8.2 函数调用

功能:创建C++函数时,会定义函数的任务,然后通过调用函数来完成已定义的任务。当程序调用函数时,程序控制权会被转移给被调用的函数。被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束标记花括号位置时,会把程序控制权交还给主程序。调用函数时,传递所需参数,如果函数返回一个值,则可以存储该返回值。

语法:函数名(参数)。

具体调用方法见例1-30。

例1-30:函数调用。

    #include<iostream>
    using namespace std;
    //函数定义
    int add(int num1, int num2) //定义中的num1、num2称为形式参数,简称形参
    {
       int sum = num1 + num2;
       return sum;
    }
    int main() {
       int a = 10;
       int b = 10;
       //调用add()函数
       int sum = add(a, b);//调用时的a、b称为实际参数,简称实参
       cout << "sum = " << sum << endl;
       a = 100;
       b = 100;
       sum = add(a, b);
       cout << "sum = " << sum << endl;
       return 0;
    }

例1-30运行结果如图1-49所示。

图1-49 例1-30运行结果

总结:函数定义里圆括号内的参数称为形式参数(简称形参),函数调用时传入的参数称为实际参数(简称实参)。

· 1.8.3 值传递

C语言的参数传递方法有3种:值传递、地址传递、引用传递,具体如下。

值传递:在函数调用时,实参以数值形式传给形参。在进行值传递时,如果形参发生改变,并不会影响实参。

地址传递:在调用函数的时候,将参数的值所在的地址复制一份过去。因此,被调用函数对参数地址的值进行修改会影响原来的值。

地址传递是比较难理解的概念,对此进行对比说明:关于值传递,可这样去理解,子函数以形参形式从主函数中传入数据,但这个数据只和形参发生传值后就立刻返回,而后子函数发生的数据变化不会影响主函数的数据;关于地址传递,子函数以形参形式从主函数中传入的是存放数据的地址,因此子函数的数据变化就是主函数的数据变化。

进一步通过“孙悟空”和“杨戬”来举例说明。定义孙悟空为一个变量,杨戬为一个函数,变量孙悟空可利用分身术产生一个形参传递给函数杨戬,这时候为值传递。函数杨戬怎样收拾变量孙悟空的分身(形参,只传递值的副本)都不会对变量孙悟空本身产生影响,但如果变量孙悟空把真身的地址告诉了函数杨戬(地址传递,传递变量孙悟空在内存中的地址),函数杨戬就可以根据地址找到变量孙悟空本身,这时候函数杨戬对变量孙悟空的操作就会改变孙悟空的值了。

引用传递:引用是变量的一个别名,调用别名和调用变量是完全一样的,其效果和地址传递一样。

这里先对值传递进行举例解释,见例1-31。地址传递举例请见1.10.4小节,引用传递举例请见2.2.2小节。

例1-31:值传递。

    #include<iostream>
    using namespace std;
    void swap1(int num1, int num2)//num1和numb2为形参
    {
           cout<<"swap1中参数交换前: num1= " << num1 <<" num2 ="<<num2<< endl;
           int temp = num1;
           num1 = num2;
           num2 = temp;
           cout<<"swap1中参数交换后: num1= " << num1 <<" num2 ="<<num2<< endl;
           return ; //当函数声明时,如果函数返回类型为void,则该函数不需要返回值,此处可不写return
    }
    int main() {
           int a = 10;
           int b = 20;
           swap1(a, b);//调用函数swap(), a和b为实参
           cout << "调用swap1交换后main中的 a= " << a<<" b="<< b<< endl;
           return 0;
    }

例1-31运行结果如图1-50所示。

C:\Users\user\AppData\Local\Temp\1592442152(1).png

图1-50 例1-31运行结果

总结:在进行值传递时,形参是改变不了实参(传递前的变量原值)的。

· 1.8.4 函数的常见样式

常见的函数样式有以下4种。

无参(数)无返(回值)。

有参无返。

无参有返。

有参有返。

具体见例1-32。

例1-32:函数常见样式。

    #include<iostream>
    using namespace std;
    //函数常见样式
    //1.无参无返
    void test01()
    {
       void a = 10; //无类型不可以创建变量,原因是它无法分配内存
       cout << "this is test01" << endl;
       test01();//函数调用
    }
    //2.有参无返
    void test02(int a)
    {
       cout << "this is test02" << endl;
       cout << "a = " << a << endl;
    }
    //3.无参有返
    int test03()
    {
       cout << "this is test03 " << endl;
       return 10;
    }
    //4.有参有返
    int test04(int a, int b)
    {
       cout << "this is test04 " << endl;
       int sum = a + b;
       return sum;
    }

· 1.8.5 函数的声明

作用:告诉编译器函数名称和如何调用函数。函数的实际主体可以单独定义。

函数可以声明多次,但是函数只能定义一次。

在函数的声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面也是有效的声明。

    int max(int, int);

具体声明方法见例1-33。

例1-33:函数的声明。

    #include<iostream>
    using namespace std;
    //声明可以多次,定义只能一次
    int max(int a, int b);
    int max(int a, int b);
    int main() {
       int a = 100;
       int b = 200;
      cout << max(a, b) << endl;
       return 0;
    }
    //定义
    int max(int a, int b)
    {
       return a > b ? a : b;
    }

例1-33运行结果如图1-51所示。

图1-51 例1-33运行结果

· 1.8.6 外部文件

作用:调用外部文件可以让代码结构更加清晰,也就是所谓的用分文件保存源文件代码。

函数分文件编写一般有4个步骤。

创建扩展名为.h的头文件。

创建扩展名为.cpp的源文件。

在头文件中写函数的声明。

在源文件中写函数的定义。

具体使用方法见例1-34。

例1-34:外部文件。

    //swap.h文件
    #include<iostream>
    using namespace std;
    //实现两个数字交换的函数声明
    void swap(int a, int b);
    //swap.cpp文件
    #include "swap.h"
    void swap(int a, int b)
    {
       int temp = a;
       a = b;
       b = temp;
       cout << "a = " << a << endl;
       cout << "b = " << b << endl;
    }
    //main()函数文件
    #include "swap.h"
    int main() {
       int a = 100;
       int b = 200;
      swap(a, b);
       return 0;
    }