通过地址传参

前情回顾

在前面的课程中,我们已经学习了两种向函数传递实参的方式:

  • 值传递(《函数形参与实参简介》)
  • 引用传递(《通过左值引用传参》)

下面通过一段示例代码比较三者(值传递、引用传递、地址传递):

#include <iostream>
#include <string>

void printByValue(std::string val)      // 形参为实参的副本
{
    std::cout << val << '\n';
}

void printByReference(const std::string& ref) // 形参为实参的引用
{
    std::cout << ref << '\n';
}

void printByAddress(const std::string* ptr)   // 形参为保存实参地址的指针
{
    std::cout << *ptr << '\n';
}

int main()
{
    std::string str{ "Hello, world!" };

    printByValue(str);      // 值传递:拷贝 str
    printByReference(str);  // 引用传递:无拷贝
    printByAddress(&str);   // 地址传递:无拷贝

    return 0;
}

地址传递(pass by address)

地址传递通过指针完成:

  • 调用者把对象的地址(指针)传给函数。
  • 形参为指针类型,保存该地址。
  • 函数通过解引用指针访问原对象。

关键点

  • 无对象拷贝:仅拷贝地址(4 或 8 字节),速度极快。
  • 可修改实参:若指针指向非 const 对象,函数可修改原对象。
  • 需显式取址:调用时用 & 获取地址,或先存于指针变量再传。

示例:修改实参

void changeValue(int* ptr) { *ptr = 6; }

int main() {
    int x{ 5 };
    changeValue(&x); // x 变为 6
}

为什么使用地址传递

  • 避免昂贵拷贝(如 std::string)。
  • 需要修改实参
  • 接口必须与 C 兼容 或需要指针语义(如动态数组)。

const 指针形参

  • 只读:形参声明为 const T* 防止修改实参。
  • 可写:形参为 T* 允许修改。

不推荐将指针形参本身声明为 const(如 T* const),除非确有必要;否则会增加视觉噪音,掩盖“是否可改对象”这一关键信息。

void foo(const char* src, char* dst); // 简洁清晰
void foo(const char* const src, char* const dst); // 噪音大

空指针检查

地址传递需防止解引用空指针:

void print(int* ptr) {
    if (!ptr) return; // 空指针直接返回
    std::cout << *ptr << '\n';
}

更严谨:用 assert 断言非空(调试期)并提前返回(发布期)。

#include <cassert>
void print(const int* ptr) {
    assert(ptr); // 调试期断言
    if (!ptr) return;
    std::cout << *ptr << '\n';
}

地址传递 vs 引用传递

特性地址传递引用传递
语法&obj / ptr直接传对象
可空是(需检查)
可接受右值是(const 引用)
可重新指向
语义直观性低(需 & 和 *)

现代 C++ 准则:

  • 能引用就引用,必须指针才指针
  • 字符串形参优先 std::string_view(值传递),次选 const std::string&

结论

地址传递通过指针实现,既避免拷贝又能修改实参,但需手动处理空指针与语法噪音。除非接口需要,否则优先使用引用传递。

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"