MST

星途 面试题库

面试题:C++ 按引用传递与指针传递在修改实参时的深度对比

在C++中,按引用传递和按指针传递都能修改实参。请详细阐述两者在修改实参时的原理差异,包括内存寻址方式、语法特点、使用场景以及可能引发的错误。并通过代码示例展示在复杂数据结构(如自定义链表)中,使用引用传递和指针传递分别修改节点数据的实现方式及效果。
25.0万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

1. 原理差异

  • 内存寻址方式
    • 按引用传递:引用本质上是一个别名,它和被引用的对象共享同一块内存地址。在函数调用时,编译器会确保引用始终指向传递进来的实参对象,无需额外的间接寻址。
    • 按指针传递:指针存储的是对象的内存地址。在函数内部通过指针访问对象时,需要先获取指针所指向的地址,然后通过该地址访问对象,这涉及到间接寻址操作。
  • 语法特点
    • 按引用传递:在函数声明和定义中,参数列表使用 & 符号来标识引用参数。例如 void func(int& num)。在调用函数时,直接传递变量名,无需取地址操作。
    • 按指针传递:在函数声明和定义中,参数列表使用 * 符号来标识指针参数。例如 void func(int* num)。在调用函数时,需要传递变量的地址,使用 & 符号取地址。
  • 使用场景
    • 按引用传递:适用于需要修改实参且希望代码更简洁、直观的场景,尤其是在处理对象时,避免了指针可能带来的空指针等问题。常用于函数需要返回多个值,通过引用参数来“输出”结果。
    • 按指针传递:适用于需要动态分配内存、处理可能为空的对象或者需要进行指针运算(如数组遍历)的场景。在实现链表、树等数据结构的操作时经常使用。
  • 可能引发的错误
    • 按引用传递:由于引用必须初始化且不能重新赋值,一旦初始化后就不会变为空引用。但是如果传递的实参本身是一个无效的对象,那么引用操作可能会导致未定义行为。
    • 按指针传递:空指针是常见问题,如果在使用指针前没有检查其是否为空,对空指针进行解引用操作会导致程序崩溃。另外,指针运算时如果越界,也会引发未定义行为。

2. 代码示例 - 自定义链表

// 定义链表节点结构体
struct ListNode {
    int data;
    ListNode* next;
    ListNode(int val) : data(val), next(nullptr) {}
};

// 使用引用传递修改节点数据
void modifyNodeDataByReference(ListNode& node, int newData) {
    node.data = newData;
}

// 使用指针传递修改节点数据
void modifyNodeDataByPointer(ListNode* node, int newData) {
    if (node != nullptr) {
        node->data = newData;
    }
}

int main() {
    ListNode* head = new ListNode(10);
    // 使用引用传递修改节点数据
    modifyNodeDataByReference(*head, 20);
    // 使用指针传递修改节点数据
    modifyNodeDataByPointer(head, 30);
    delete head;
    return 0;
}

在上述代码中,modifyNodeDataByReference 函数通过引用传递修改节点数据,直接操作节点对象。modifyNodeDataByPointer 函数通过指针传递修改节点数据,需要先检查指针是否为空,再进行操作。两种方式都能成功修改链表节点的数据,但原理和语法略有不同。