C++函数参数 #

一、参数传递概述 #

C++支持多种参数传递方式:

  • 值传递
  • 指针传递
  • 引用传递

二、值传递 #

2.1 基本概念 #

值传递会创建参数的副本,函数内修改不影响原值。

cpp
void increment(int x) {
    x++;  // 修改的是副本
}

int main() {
    int a = 10;
    increment(a);
    std::cout << a << std::endl;  // 10,未改变
    return 0;
}

2.2 适用场景 #

cpp
// 适合小型数据类型
void process(int x, double y, char c);

// 不适合大型对象
void processBigObject(BigObject obj);  // 复制开销大

2.3 优点与缺点 #

cpp
// 优点:安全,不会意外修改原值
void safeFunction(int x) {
    x = 100;  // 只修改副本
}

// 缺点:复制开销
void inefficient(const std::string str) {  // 复制整个字符串
    std::cout << str << std::endl;
}

三、指针传递 #

3.1 基本概念 #

指针传递传递的是地址,可以修改原值。

cpp
void increment(int* x) {
    (*x)++;  // 修改原值
}

int main() {
    int a = 10;
    increment(&a);
    std::cout << a << std::endl;  // 11
    return 0;
}

3.2 空指针检查 #

cpp
void process(int* ptr) {
    if (ptr == nullptr) {
        return;  // 安全检查
    }
    *ptr = 100;
}

3.3 交换两个值 #

cpp
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;
    swap(&x, &y);
    std::cout << x << ", " << y << std::endl;  // 20, 10
    return 0;
}

3.4 数组传递 #

cpp
// 数组作为参数时退化为指针
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        std::cout << arr[i] << " ";
    }
}

// 等价于
void printArray(int* arr, int size) {
    for (int i = 0; i < size; i++) {
        std::cout << arr[i] << " ";
    }
}

四、引用传递 #

4.1 基本概念 #

引用传递是原值的别名,修改会影响原值。

cpp
void increment(int& x) {
    x++;  // 修改原值
}

int main() {
    int a = 10;
    increment(a);
    std::cout << a << std::endl;  // 11
    return 0;
}

4.2 交换两个值 #

cpp
void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 10, y = 20;
    swap(x, y);
    std::cout << x << ", " << y << std::endl;  // 20, 10
    return 0;
}

4.3 常量引用 #

cpp
// 使用const引用避免复制,同时禁止修改
void printString(const std::string& str) {
    std::cout << str << std::endl;
    // str = "new";  // 错误:不能修改
}

int main() {
    std::string s = "Hello";
    printString(s);
    printString("World");  // 可以接受临时对象
    return 0;
}

4.4 引用传递的优点 #

cpp
// 1. 避免复制开销
void processBigObject(const BigObject& obj);

// 2. 语法简洁
void swap(int& a, int& b);  // 比 swap(int* a, int* b) 更简洁

// 3. 不能为空
void func(int& ref);  // ref一定有效

五、const参数 #

5.1 const值参数 #

cpp
// const值参数:函数内不能修改
void func(const int x) {
    // x = 10;  // 错误
    std::cout << x << std::endl;
}
// 注意:const值参数对调用者无意义,只影响函数内部

5.2 const指针参数 #

cpp
// 指向常量的指针
void func(const int* ptr) {
    // *ptr = 10;  // 错误:不能修改指向的值
    ptr = nullptr;  // 可以修改指针本身
}

// 常量指针
void func(int* const ptr) {
    *ptr = 10;  // 可以修改指向的值
    // ptr = nullptr;  // 错误:不能修改指针
}

// 指向常量的常量指针
void func(const int* const ptr) {
    // 都不能修改
}

5.3 const引用参数 #

cpp
// 最常用的只读参数形式
void printVector(const std::vector<int>& vec) {
    for (const auto& x : vec) {
        std::cout << x << " ";
    }
}

六、默认参数 #

6.1 基本语法 #

cpp
void printMessage(std::string message, int times = 1) {
    for (int i = 0; i < times; i++) {
        std::cout << message << std::endl;
    }
}

int main() {
    printMessage("Hello");      // 使用默认值1
    printMessage("World", 3);   // 指定times为3
    return 0;
}

6.2 默认参数规则 #

cpp
// 默认参数必须从右向左连续
void func(int a, int b = 2, int c = 3);  // 正确
// void func(int a = 1, int b, int c = 3);  // 错误

// 声明和定义分离时,默认参数在声明中
void func(int a, int b = 10);  // 声明

void func(int a, int b) {      // 定义(不要重复默认参数)
    std::cout << a << ", " << b << std::endl;
}

6.3 多个默认参数 #

cpp
void createWindow(std::string title, 
                  int width = 800, 
                  int height = 600, 
                  bool fullscreen = false);

int main() {
    createWindow("My Window");                    // 全部使用默认值
    createWindow("My Window", 1024);              // 指定width
    createWindow("My Window", 1024, 768);         // 指定width和height
    createWindow("My Window", 1024, 768, true);   // 全部指定
    return 0;
}

6.4 默认参数与重载 #

cpp
// 可能产生歧义
void func(int a);
void func(int a, int b = 10);

int main() {
    func(5);  // 错误:调用哪个?
    return 0;
}

七、可变参数 #

7.1 C风格可变参数 #

cpp
#include <cstdarg>

int sum(int count, ...) {
    int total = 0;
    va_list args;
    va_start(args, count);
    
    for (int i = 0; i < count; i++) {
        total += va_arg(args, int);
    }
    
    va_end(args);
    return total;
}

int main() {
    std::cout << sum(3, 1, 2, 3) << std::endl;      // 6
    std::cout << sum(5, 1, 2, 3, 4, 5) << std::endl; // 15
    return 0;
}

7.2 初始化列表(C++11) #

cpp
#include <initializer_list>

int sum(std::initializer_list<int> nums) {
    int total = 0;
    for (int x : nums) {
        total += x;
    }
    return total;
}

int main() {
    std::cout << sum({1, 2, 3}) << std::endl;        // 6
    std::cout << sum({1, 2, 3, 4, 5}) << std::endl;  // 15
    return 0;
}

7.3 可变参数模板(C++11) #

cpp
template<typename... Args>
void print(Args... args) {
    (std::cout << ... << args) << std::endl;  // C++17折叠表达式
}

int main() {
    print(1, 2, 3);              // 123
    print("Hello", " ", "World"); // Hello World
    return 0;
}

八、参数传递选择 #

8.1 选择指南 #

参数类型 传递方式 示例
小型基本类型 值传递 int, char, double
大型对象 const引用 const std::string&
需要修改 引用或指针 int&int*
可选参数 指针(可为nullptr) int* ptr = nullptr

8.2 示例 #

cpp
// 小型类型:值传递
int add(int a, int b);

// 大型对象:const引用
void process(const std::string& str);
void process(const std::vector<int>& vec);

// 需要修改:引用
void modify(std::string& str);
void fill(std::vector<int>& vec);

// 可选参数:指针
void process(int* ptr = nullptr) {
    if (ptr) {
        // 处理ptr
    } else {
        // ptr为空的处理
    }
}

九、最佳实践 #

9.1 使用const引用传递大对象 #

cpp
// 不推荐
void process(std::string str);  // 复制开销

// 推荐
void process(const std::string& str);  // 无复制

9.2 避免使用非const指针参数 #

cpp
// 不推荐:调用者不知道值会被修改
void func(int* x);

// 推荐:使用引用
void func(int& x);

// 或者使用输出参数命名
void getValue(int* outValue);

9.3 合理使用默认参数 #

cpp
// 好的默认参数
void openFile(const std::string& path, 
              std::ios::openmode mode = std::ios::in);

// 不好的默认参数(容易出错)
void process(int* ptr = nullptr);  // 调用者可能忘记检查

十、总结 #

参数传递方式 #

方式 语法 特点
值传递 T 复制,安全
指针传递 T* 可为空,可修改
引用传递 T& 不能为空,可修改
const引用 const T& 只读,高效

最佳实践 #

  1. 小类型用值传递
  2. 大对象用const引用
  3. 需要修改用引用
  4. 可选参数用指针
  5. 合理使用默认参数

下一步,让我们学习函数重载!

最后更新:2026-03-26