需要预先了解:


1. 形参与实参

1.1 形式参数(Parameter)

形参是函数定义或函数声明时,在参数列表中声明的变量,是函数内部用来接收外部数据的占位符。

  • 函数未被调用时,不占用实际内存
  • 函数被调用时,创建并初始化
  • 函数执行结束后立即销毁

1.2 实际参数(Argument)

实参是调用函数时传递给函数的具体数据。

// a、b是形参
int add(int a, int b)
{
    return a + b;
}

int main()
{
    int x = 10, y = 20;
    
    // x+y、x*y是实参
    int res = add(x + y, x * y);
    return 0;
}

2. 值传递

形参是实参的一份独立拷贝,形参与实参占有不同的内存地址。

  • 函数内对形参的修改都不会作用于外部的实参
  • 如果参数的内存占用过大,频繁地拷贝会导致明显的性能问题
void swap(int x, int y)
{
    int temp = x;
    x = y;
    y = x;
}

int main()
{
    int a = 10, b = 20;
    swap(a, b);
    std::cout << "a: "<< a << ",b: " << b << std::endl;
}

3. 指针传递与引用传递

指针传递中,形参是一个指针变量,实参是地址值,因此可以正确修改外部空间,形参仅对大小4字节或8字节的地址值进行拷贝。

swap(int* x, int* y)
{
    int temp = *x;
    *x = *y;
    *y = temp;
}

swap(&a, &b);

引用传递中,形参是实参的引用,形参与实参指向同一块内存空间,可以通过修改形参来直接修改外部的实参,拷贝花销与指针相同。

swap(int& x, int& y)
{
    int temp = x;
    x = y;
    y = temp;
}

swap(a, b);

4. 结论

4.1 特征总结

  1. 指针传递与引用传递需要传输8字节的地址值,值传递需要传输实参的全部数据
  2. 指针传递和引用传递可以实现对实参的修改,值传递不可以

4.2 传参选择

  1. 如果需要修改实参的值,必须选择指针传递或引用传递
  2. 如果不需要修改实参的值,按照以下原则选择:
    • 如果实参数据量较小(内置数据类型int、float等,或是很小的结构体),选择值传递
    • 如果实参数据量较大(大型结构体、类对象等),选择指针传递或引用传递;此时可以用const修饰参数,防止在函数内部修改实参的值:
void print(const char* str)
{
    std::cout << str << std::endl;
}

void print(const std::string& str)
{
    std::cout << str << std::endl;
}