C++函数调用中指针参数*p与*&p的深度解析与避坑指南

1次阅读
没有评论

共计 2142 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

指针参数在 C ++ 中的重要性

指针是 C ++ 中一个非常强大的工具,它允许我们直接操作内存地址,实现高效的数据访问和修改。在函数调用中,指针参数尤为重要,因为它能够让我们在函数内部修改外部变量的值,而不仅仅是传递一个副本。这对于处理大量数据或需要频繁修改的场景非常有用。

C++ 函数调用中指针参数 * p 与 *& p 的深度解析与避坑指南

指针参数的使用场景包括但不限于:

  • 需要在函数内部修改外部变量的值
  • 传递大型数据结构(如数组、结构体)以避免拷贝开销
  • 实现动态内存分配和管理

p 与 &p 的底层内存操作机制

* p 传参方式

当我们将指针作为参数传递给函数时,实际上是传递了指针的副本。这意味着函数内部对指针本身的修改(如改变指针的指向)不会影响外部的指针。例如:

void func(int* p) {p = new int(20); // 修改的是局部指针 p 的副本
}

int main() {int* ptr = new int(10);
    func(ptr);
    // ptr 仍然指向原来的内存,值为 10
    delete ptr;
    return 0;
}

*&p 传参方式

当我们将指针的引用作为参数传递时,函数内部对指针的任何修改都会反映到外部的指针上。这种方式允许我们在函数内部改变指针的指向。例如:

void func(int*& p) {p = new int(20); // 修改的是外部指针本身
}

int main() {int* ptr = new int(10);
    func(ptr);
    // ptr 现在指向新的内存,值为 20
    delete ptr;
    return 0;
}

典型场景代码示例

场景 1:修改指针指向

// 使用 * p 方式 - 无法修改外部指针
void changePointer1(int* p) {p = new int(100); // 仅修改局部副本
}

// 使用 *&p 方式 - 可以修改外部指针
void changePointer2(int*& p) {p = new int(200); // 修改原始指针
}

int main() {int* ptr = new int(10);

    changePointer1(ptr);
    std::cout << *ptr << std::endl; // 输出 10

    changePointer2(ptr);
    std::cout << *ptr << std::endl; // 输出 200

    delete ptr;
    return 0;
}

场景 2:修改指针内容

// 两种方式都可以修改指针指向的内容
void modifyContent1(int* p) {*p = 30; // 修改指针指向的值}

void modifyContent2(int*& p) {*p = 40; // 同样可以修改指针指向的值}

int main() {int* ptr = new int(10);

    modifyContent1(ptr);
    std::cout << *ptr << std::endl; // 输出 30

    modifyContent2(ptr);
    std::cout << *ptr << std::endl; // 输出 40

    delete ptr;
    return 0;
}

场景 3:多级指针操作

// 使用 ** p 方式
void multiLevel1(int** p) {*p = new int(50); // 修改一级指针的指向
}

// 使用 *&* p 方式
void multiLevel2(int*& p) {p = new int(60); // 直接修改指针本身
}

int main() {
    int* ptr = nullptr;

    // 方法 1:使用二级指针
    multiLevel1(&ptr);
    std::cout << *ptr << std::endl; // 输出 50
    delete ptr;

    // 方法 2:使用指针引用
    ptr = nullptr;
    multiLevel2(ptr);
    std::cout << *ptr << std::endl; // 输出 60
    delete ptr;

    return 0;
}

常见错误用法及后果

  1. 内存泄漏 :在函数内部分配内存但忘记释放,或者外部指针被覆盖导致无法释放原有内存。
void badExample(int*& p) {p = new int(100); // 分配新内存
    // 如果外部指针原来指向的内存没有被释放,就会泄漏
}
  1. 野指针 :在函数内部释放了指针指向的内存,但没有将指针置空,导致后续访问非法内存。
void dangerousExample(int* p) {
    delete p; // 释放内存
    // 没有将外部的 p 置空,现在 p 是野指针
}
  1. 无效修改 :错误地认为 * p 方式可以修改指针的指向,导致逻辑错误。
void misunderstanding(int* p) {p = new int(200); // 这个修改不会影响外部指针
    // 这里不仅无法修改外部指针,还会造成内存泄漏
    // 因为分配的内存没有指针指向它了
}

最佳实践建议

  1. 何时使用 *p
  2. 只需要读取或修改指针指向的内容时
  3. 不需要改变指针本身的指向时
  4. 处理简单的值传递场景

  5. 何时使用 *&p

  6. 需要在函数内部改变指针的指向时
  7. 实现类似 ” 输出参数 ” 的功能时
  8. 处理复杂的内存管理逻辑时

  9. 通用建议

  10. 明确函数是否需要修改指针本身
  11. 对于可能分配内存的函数,考虑使用智能指针
  12. 在函数文档中清楚地说明参数的使用方式
  13. 释放内存前检查指针是否为空
  14. 考虑使用 const 修饰符来保护不需要修改的指针

思考题

  1. 如何设计一个安全的指针参数传递接口,既能修改指针指向的内容,又能防止内存泄漏和野指针问题?

  2. 在多线程环境下使用指针参数时,有哪些额外的注意事项需要考虑?特别是当多个线程可能同时访问或修改同一个指针时。

正文完
 0
评论(没有评论)