• 【温故而知新-02】C++函数参数传递


    一、开篇

    在C++中,函数参数的传递有六种不同的情况:

    (1)传值参数

    (2)指针形参

    (3)传引用参数

    (4)数组形参

    (5)可变形参函数

    (6)省略符形参

    在实际C++使用中,前四种是经常使用到的参数传递方式,后两种可能使用频次不多,但是在阅读其他项目源码时可能会遇见!

    二、参数传递基础知识

    • 函数的参数传递有两个概念:【形参和实参的参数传递】 、【 函数调用过程】。

    • 每次函数调用,都会重新创建函数的形参,并使用传入的实参对形参进行初始化(形参初始化的机理与变量初始化一样)。

    • 重磅一句话:函数形参的类型决定了形参与实参的交互方式。如果形参的类型是引用类型,那么它将绑定到对应的实参上;否则,会将实参的值拷贝后赋值给形参。

    总结一下:

    1、函数调用需要开销。

    2、函数形参的类型决定了函数调用时,形参与实参的交互方式:拷贝赋值or引用绑定。

    注:站在函数参数传递方式的角度考虑:拷贝赋值又叫做值传递;引用绑定又叫做引用传递

    三、传值参数

    ​ 当初始化一个非引用类型的变量时,初始值会被拷贝给变量。对变量的改动不会影响初始值,因为此刻存在两份值对象。所以,在函数中对形参所做的所有改动操作都不会影响实参。

    四、指针形参

    ​ 函数调用时,指针的行为与其他非引用类型一样。在执行指针拷贝操作时,拷贝的是指针的值,拷贝后,两个指针是不同的指针。但是,指针允许间接的访问到它所指的对象,所以通过指针可以修改它所指对象的值。

    在C语言中,常常使用指针类型的形参访问函数外部的对象。在C++语言中,建议使用引用类型的形参替代指针

    五、传引用参数

    (5-1)使用引用避免拷贝

    拷贝大的类类型对象或者容器对象是比较低效的操作,甚至有些类类型(包括IO类型在内)根本就不支持拷贝操作,此时把此类类型作为函数的参数传递,将会引发错误。因此,当某种类型不支持拷贝操作时,函数只能通过引用形参访问该类型的对象。

    如果在函数中不会改变引用形参的值,最好将其声明为常量引用,这么做是一种比较规范的代码设计。

    (5-2)使用引用形参返回额外的参数信息

    通常, 一个函数只能返回一个值,但是有时候我们的函数需要同时返回多个值,此时则可以使用引用来返回多个结果。

    通过函数引用形参返回多个参数。如下代码示例:

    #include <iostream>
    
    class student{
    public:
        void getStudentInfo(std::string &name,int &age)
        {
            name = m_name;
            age  = m_age;
        }
        
    private:
        std::string m_name = "zhangSan";
        int m_age = 25;
    };
    
    int main() {
        student stu_1;
        std::string stu1_name;
        int stu1_age;
    
        stu_1.getStudentInfo(stu1_name,stu1_age);
    
        std::cout<<"student1_name:"<<stu1_name<<std::endl;
        std::cout<<"student1_age:"<<stu1_age<<std::endl;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    六、数组形参

    数组作为函数的参数传递时,有两个重要特性:

    (1)不允许拷贝数组。

    (2)使用数组时,编译器通常会将其转换为指针

    不能以值传递的方式传递数组,但是函数形参可以写成类似于数组的形式,如下代码:

    void print_info(const int *);
    
    void print_info(const int[]);
    
    void print_info(const int[10]);  //数组的大小对函数调用没有影响
    
    • 1
    • 2
    • 3
    • 4
    • 5

    因为传递数组是以指针的方式传递,所以我们在函数中是不知道数组的大小,因此需要函数调用者传递额外的数组大小信息。计算数组的大小有三种方式:
    (1)使用标记指定数组长度。
    这种方式,必须要求数组有一个结束标记,这种对于字符串非常有用,但是对于全是int类型的数组无效。例如以下代码:

    void print_array(char *cp)
    {  
      if(cp)
      {
        while(*cp)
        {
          std::cout<<*cp++;
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    (2)使用标准库+数组开始位置和结束位置计算数组大小

    例如以下代码:

    voit print_array(int *start, int *end)
    {
      while(start != end)
      {
        std::cout<< *start++ <<std::endl;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    对于以上代码,在调用print_array()函数的时候,需要传入一个数组的首元素的指针和一个指向数组尾后元素的指针。

    int main()
    {
      int num[5] = {1,4,3,2,5};
      
      //调用标准库函数begin和end作为print_array()的实参。
      print_array(begin(num), end(num));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    end()函数用于返回一个数组尾元素的下一个位置。

    (3)显式传递一个表示数组大小的参数

    例如以下代码:

    void print_array(const int ar[],size_t size)
    {
      for(size_t i = 0; i < size ; ++i)
      {
        std::cout << ar[i] << std::endl;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在函数中使用数组时必须保证对数组的操作不会越界。

    七、可变形参函数

    在实际开发中,有时我们不能预测传递给函数的实参个数,且我们希望把这一系列的操作归一到一个函数中,在C++11标准中,提供了两种方法实现这种可变形参个数的函数实现方法:

    (7-1)initializer_lit形参

    #include <iostream>
    using namespace std;
    
    void print_error_msg(initializer_list<string> error_info)
    {
        for(auto begin = error_info.begin();begin != error_info.end(); begin++)
        {
            cout<< *begin <<endl;
        }
        cout<< "----------------"<<endl;
    }
    
    int main() 
    {
        int param_num = 3;
        do{
            if(param_num == 3)
            {
                print_error_msg({"03","error","information"});
            }
            else if(param_num == 2)
            {
                print_error_msg({"02","error"});
            }
            else if(param_num == 1)
            {
                print_error_msg({"01"});
            }
    
        }while ((param_num--) > 0);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    结果如下:

    initializer_list的使用条件和注意事项:

    (1)函数中全部实参的类型完全相同!

    (2)initializer_list对象中元素永远是常量值,无法改变initializer_list对象中元素的值。

    (7-2)省略符形参

    省略符形参是为了给C++程序访问特殊的C代码而设置的,这些代码会使用到C标准库中varargs功能。

    例如如下代码:

    #include <stdio.h>
    #include <stdargs.h>
    int sum_ops(int num, ...) {
        va_list valist;
    
        int ret = 0;
    
        va_start(valist, num);
        for (int i = 0; i < num; i++) {
            ret += va_arg(valist, int);
        }
        va_end(valist);
        return ret;
    }
    
    int main() {
        printf("2+3 = %d\n", sum(2, 2, 3));
        printf("1+2+3+4 =  %d\n", sum(4, 1, 2, 3, 4));
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    省略符形参只能出现在形参列表的最后一个位置,形式有两种:

    
    //这种形式指定了ops函数的部分参数的类型,对应于这些形参的实参将会执行正常的类型检查,
    //省略符形参所对应的实参无须类型检查。1void ops(param1,param2,...);
    
    //形参声明后面的逗号是可选的。2void ops(...);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    省略符形参应该仅仅用于C和C++通用的类型。需要注意的是:大多数类类型的对象在传递给省略符形参时都无法正确拷贝!!!因此,不能将类类型以省略符形参的方式进行传递。

  • 相关阅读:
    【LeetCode热题100】【图论】岛屿数量
    UE XML解析
    ArcGIS API for JavaScript实现要素服务query接口的功能
    (八)vtk常用类的常用函数介绍(附带代码示例)
    网络安全技术指南 103.91.209.X
    大一学生Web课程设计 美食主题网页制作(HTML+CSS+JavaScript)
    Java:实现使用快速傅里叶变换非常有效地乘2个复多项式算法(附完整源码)
    java构建树(tree)型结构,只循环一次非递归
    20220910编译ITX-3588J的Buildroot的系统2b(编译Kernel)
    VUE3 页面路由 router
  • 原文地址:https://blog.csdn.net/iriczhao/article/details/125629112