函数的参数传递-值传递

一、函数参数传递的概念

在函数未被调用时,函数的形参不会占用内存空间,也没有实际的值。只有在函数被调用时,才为形参分配存储单元。每个实参都是一个表达式,其类型必须与形参的类型相符。

C++ 中参数传递有两种方式:

  1. 值传递
  2. 引用传递

本节先讲解值传递。

二、值传递的原理

值传递是指:当发生函数调用时,为形参分配内存空间,并用实参的值来初始化形参(直接将实参的值复制给形参)。这一过程是参数值的单向传递。

一旦形参获得值后,形参和实参便脱离关系。此后无论形参发生了怎样的改变,都不会影响到实参。

三、值传递的特点

特性说明
传递方向单向传递(实参 → 形参)
内存关系形参和实参占用不同的内存空间
形参修改只修改形参自身的值,不影响实参
初始化方式实参的值复制给形参,等价于 形参 = 实参
适用场景不需要修改实参、数据量较小的场合

四、示例:交换两个整数(值传递方式)

以下代码尝试通过值传递交换两个整数,但无法成功:

#include <iostream>
using namespace std;

void swap(int a, int b)
{
    int z = a;
    a = b;
    b = z;
    // cout << "函数内 a,b " << a << " " << b << "\n";
}

int main()
{
    int a = 2;
    int b = 4;
    cout << "调用前 a,b " << a << " " << b << "\n";   // 输出 2 4
    swap(a, b);
    cout << "调用后 a,b " << a << " " << b << "\n";   // 仍然输出 2 4
    return 0;
}Code language: PHP (php)

分析:

  1. main 中 a = 2, b = 4。
  2. 调用 swap(a, b) 时,为形参 a、b 分配内存,并将实参的值复制过去。此时形参 a = 2, 形参 b = 4。
  3. swap 函数内部交换了形参 a 和 b 的值,形参变为 a = 4, b = 2。
  4. swap 执行完毕,形参所占内存被释放。
  5. main 中的实参 a 和 b 自始至终没有被修改,仍然是 a = 2, b = 4。

五、代码示例

以下代码进一步演示值传递的各种情况:

#include <iostream>
using namespace std;

// 示例1:基本类型的值传递
void modifyInt(int x)
{
    x = 100;
    cout << "modifyInt 内 x = " << x << endl;   // 输出 100
}

// 示例2:值传递不影响实参
void trySwap(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;
    cout << "trySwap 内 a = " << a << ", b = " << b << endl;
}

// 示例3:值传递用于计算(不修改实参)
int square(int x)
{
    x = x * x;       // 修改的是形参 x
    return x;
}

// 示例4:结构体的值传递
struct Point
{
    int x;
    int y;
};

void movePoint(Point p)
{
    p.x += 10;
    p.y += 10;
    cout << "movePoint 内 p.x = " << p.x << ", p.y = " << p.y << endl;
}

// 示例5:数组作为参数(注意:数组名退化为指针,不属于值传递)
void printArray(int arr[5])
{
    arr[0] = 999;    // 这会改变原数组!
    for (int i = 0; i < 5; i++)
        cout << arr[i] << " ";
    cout << endl;
}

// 示例6:指针的值传递(传递的是地址值本身)
void modifyPointer(int* p)
{
    p = NULL;        // 只修改了形参 p,不影响实参
}

int main()
{
    int a = 5;
    modifyInt(a);
    cout << "main 中 a = " << a << endl << endl;   // 输出 5

    int x = 2, y = 4;
    cout << "交换前 x = " << x << ", y = " << y << endl;
    trySwap(x, y);
    cout << "交换后 x = " << x << ", y = " << y << endl << endl;   // 未交换

    int num = 3;
    int result = square(num);
    cout << "square(" << num << ") = " << result << endl;
    cout << "num 仍为 " << num << endl << endl;

    Point pt = { 1, 2 };
    cout << "移动前 pt.x = " << pt.x << ", pt.y = " << pt.y << endl;
    movePoint(pt);
    cout << "移动后 pt.x = " << pt.x << ", pt.y = " << pt.y << endl << endl;

    int arr[5] = { 1, 2, 3, 4, 5 };
    cout << "原数组: ";
    printArray(arr);
    cout << "调用后 arr[0] = " << arr[0] << endl << endl;   // arr[0] 变为 999

    int val = 10;
    int* ptr = &val;
    modifyPointer(ptr);
    if (ptr != NULL)
        cout << "ptr 仍指向 val,值为 " << *ptr << endl;

    return 0;
}Code language: C++ (cpp)

六、注意事项

  • 值传递是 C++ 默认的实参-形参结合方式。
  • 值传递时,实参的值被复制给形参。对于基本类型(int、double、char 等),复制开销很小;对于大型结构体或类对象,复制开销较大,此时应使用引用传递或指针传递。
  • 值传递是单向的:形参的改变不会影响实参。
  • 如果需要在函数中修改实参的值,不能使用值传递,应使用引用传递或指针传递(下一节介绍)。
  • 数组作为函数参数时,数组名会退化为指针,传递的是数组首地址的副本,函数内通过指针可以修改数组元素。这不属于值传递,而是地址传递。
  • 指针作为形参时,传递的是指针变量的值(地址),形参获得地址的副本。修改形参指向的内容(如 *p = 10)会影响实参指向的内容,但修改形参本身(如 p = NULL)不影响实参。

七、常见错误

1. 误以为值传递能交换实参
void swap(int a, int b)
{
    int t = a; a = b; b = t;
}
int main()
{
    int x = 1, y = 2;
    swap(x, y);
    // 期望 x=2, y=1,实际 x=1, y=2
}Code language: JavaScript (javascript)

解决:使用引用传递 void swap(int& a, int& b) 或指针传递 void swap(int* a, int* b)。

2. 大型对象值传递导致性能问题
struct BigData { int data[10000]; };
void process(BigData d) { ... }   // 每次调用复制 40000 字节Code language: JavaScript (javascript)

解决:使用 const BigData& d 避免复制。

3. 混淆指针传递和值传递
void foo(int* p)
{
    p = NULL;     // 只修改了形参 p
}
int main()
{
    int x = 10;
    int* q = &x;
    foo(q);
    // q 仍然指向 x,没有被修改
}Code language: JavaScript (javascript)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注