指针变量(pointer variable)和引用变量(reference variable)在 C++ 中有一些重要的区别。引用通常比指针更安全和易于使用,因为它们必须初始化并且不能为 null。在 C++ 中,尽可能优先使用引用,只有在确实需要指针的灵活性和功能时才使用指针。

1、初始化与绑定

指针(Pointer) 可以在声明时不初始化,稍后可以指向不同的对象。引用(Reference)​必须在声明时初始化,并且一旦绑定到某个对象,就不能再绑定到其他对象。

#include <iostream>

int main() {
    int x = 5;
    int y = 10;

    // 指针示例
    int* p;  // 定义一个指向 int 类型的指针
    p = &x;  // p 指向 x
    std::cout << "p points to x: " << *p << std::endl;  // 输出 5

    p = &y;  // p 现在指向 y
    std::cout << "p points to y: " << *p << std::endl;  // 输出 10

    // 引用示例
    int& r = x;  // r 引用 x
    std::cout << "r references x: " << r << std::endl;  // 输出 5

    // r = &y;  // 错误,r 不能重新绑定到其他变量

    return 0;
}

2、可空性(Nullability)

指针​可以指向 nullptr,表示不指向任何有效对象,引用 必须引用一个有效的对象,不能为 nullptr

#include <iostream>

int main() {
    int x = 5;
    int* p = nullptr;  // p 不指向任何对象

    if (p == nullptr) {
        std::cout << "p is a null pointer and does not point to any object." << std::endl;
    }

    // 将 p 指向 x
    p = &x;
    std::cout << "p now points to x: " << *p << std::endl;  // 输出 5

    return 0;
}

3、内存地址与大小

指针 ​是一个独立的变量,拥有自己的内存地址和大小,引用 ​没有自己的内存地址,其地址和大小与所引用的对象相同。​

#include <iostream>

int main() {
    // 指针示例
    int x = 5;
    int* p = &x;

    std::cout << "Address of pointer p: " << &p << std::endl;  // 输出 p 的地址
    std::cout << "Size of pointer p: " << sizeof(p) << " bytes" << std::endl;  // 输出指针的大小

    // 引用示例
    int& r = x;

    std::cout << "Address of reference r: " << &r << std::endl;  // 输出 x 的地址
    std::cout << "Size of reference r: " << sizeof(r) << " bytes" << std::endl;  // 输出 x 的大小

    return 0;
}

4、数组与多级指针

指针 ​可以创建指向指针的指针(多级指针),也可以创建指针数组,引用 ​不能创建指向引用的引用,也不能创建引用数组。

#include <iostream>

int main() {
    int x = 5, y = 10;

    // 指向指针的指针
    int* p = &x;  // p 是指向 x 的指针
    int** pp = &p;  // pp 是指向指针 p 的指针

    std::cout << "Address of p: " << &p << std::endl;    // 输出 p 的地址
    std::cout << "Address stored in pp (which points to p): " << *pp << std::endl;  // 输出 pp 存储的 p 的地址
    std::cout << "Value of x via pp: " << **pp << std::endl;  // 输出 **pp 指向的值,即 x 的值

    // 指针数组
    int* arr[] = {&x, &y};  // arr 是一个指针数组,元素为 x 和 y 的地址
    std::cout << "arr[0] points to x: " << *arr[0] << std::endl;  // 输出 arr[0] 指向的值(x 的值)
    std::cout << "arr[1] points to y: " << *arr[1] << std::endl;  // 输出 arr[1] 指向的值(y 的值)

    // 引用示例
    int& r = x;  // r 是 x 的引用
    std::cout << "r references x: " << r << std::endl;  // 输出 r 引用的值,即 x 的值

    // 错误的代码:
    // int&& rr = r;  // 错误,不能创建引用的引用
    // int& arr[] = {r, s};  // 错误,不能创建引用数组

    return 0;
}

5、使用场景

引用 ​通常用于函数参数和返回类型,提供更简洁的语法,避免不必要的复制。指针 ​用于动态内存管理、实现复杂数据结构(如链表、树等)以及需要明确控制内存地址的场景。

#include <iostream>

void increment(int& n) {
    n++;  // 增加 n 的值
}

int main() {
    // 使用引用作为函数参数
    int num = 5;
    std::cout << "Before increment: " << num << std::endl;
    increment(num);  // 调用函数,传递引用
    std::cout << "After increment: " << num << std::endl;

    // 动态内存分配和释放
    int* p = new int(10);  // 动态分配内存并初始化为 10
    std::cout << "Dynamically allocated value: " << *p << std::endl;
    delete p;  // 释放内存

    return 0;
}