1. 定义和初始化
- 指针:
- 指针是一个变量,其存储的值是另一个变量的地址。指针的声明使用
*运算符,在使用前可以不进行初始化,即可以为nullptr或指向未定义的地址(虽然这可能导致错误)。
int* ptr; // 声明一个指向 int 类型的指针 int var = 10; ptr = &var; // 让指针 ptr 指向 var 的地址- 指针可以被重新赋值,指向不同的地址。
int anotherVar = 20; ptr = &anotherVar; // 重新赋值指针,使其指向另一个变量 - 指针是一个变量,其存储的值是另一个变量的地址。指针的声明使用
- 引用:
- 引用是另一个变量的别名,必须在声明时进行初始化,并且一旦初始化后,就不能再引用其他变量。
int var = 10; int& ref = var; // 声明一个引用 ref,它是 var 的别名- 引用的初始化方式要求其必须绑定到一个已存在的对象,不能为
nullptr或未定义的地址。
2. 语法
- 指针:
- 为了访问指针所指向的值,需要使用
*运算符进行解引用。
int value = *ptr; // 解引用指针 ptr 获取其指向的值- 指针可以通过
->运算符来访问对象的成员(如果指针指向一个对象)。
struct MyStruct { int data; }; MyStruct s; MyStruct* ptr = &s; ptr->data = 10; // 使用 -> 访问成员 - 为了访问指针所指向的值,需要使用
- 引用:
- 引用使用起来就像使用原变量一样,无需额外的运算符。
ref = 20; // 直接修改引用,实际上修改的是 var 的值
3. 内存占用
- 指针:
- 指针本身在内存中占据空间,通常在 32 位系统上是 4 字节,在 64 位系统上是 8 字节。
std::cout << sizeof(ptr) << std::endl; // 输出指针的大小 - 引用:
- 引用在实现上可能类似于指针,但在语义上它是原变量的别名,不额外占用空间,或者说引用的大小不是程序员需要关心的,编译器会处理。
4. 空值表示
- 指针:
- 可以表示空值,通常使用
nullptr或NULL表示。
int* ptr = nullptr; // 指针可以为 nullptr- 对空指针进行解引用操作会导致未定义行为,需要在使用前进行检查。
if (ptr!= nullptr) { *ptr = 10; // 先检查指针是否为空 } - 可以表示空值,通常使用
- 引用:
- 引用不允许为
nullptr或空值,它必须始终绑定到一个有效的对象。
- 引用不允许为
5. 函数参数传递
- 指针:
- 作为函数参数时,传递的是变量的地址,可以通过指针修改原变量的值,也可以在函数内修改指针的指向。
void modifyValue(int* ptr) { *ptr = 100; // 修改原变量的值 int anotherVar = 200; ptr = &anotherVar; // 修改指针的指向 } int var = 10; modifyValue(&var); - 引用:
- 作为函数参数时,传递的是变量的别名,在函数内可以修改原变量的值,但不能改变引用的绑定对象。
void modifyValue(int& ref) { ref = 100; // 修改原变量的值 } int var = 10; modifyValue(var);
6. 常量性
- 指针:
- 可以有指向常量的指针和常量指针。
const int* ptr1; // 指向常量的指针,不能通过 ptr1 修改所指对象的值 int* const ptr2 = &var; // 常量指针,不能修改 ptr2 的指向 const int* const ptr3 = &var; // 指向常量的常量指针,既不能修改指向,也不能修改所指对象的值 - 引用:
- 有常量引用,常用于函数参数传递,避免复制开销,同时保证原对象不被修改。
void printValue(const int& ref) { std::cout << ref << std::endl; // 不能修改 ref 的值 } int var = 10; printValue(var);
7. 多级表示
- 指针:
- 可以有多级指针,如二级指针
int** ptr2;表示指向指针的指针。
int var = 10; int* ptr = &var; int** ptr2 = &ptr; // 二级指针,指向指针的指针- 常用于动态内存分配,如
int** arr = new int*[n];可创建一个指针数组。
- 可以有多级指针,如二级指针
- 引用:
- 没有多级引用,如
int&& ref2;不是多级引用,而是右值引用,用于延长右值的生命周期等,与引用的概念不同。
- 没有多级引用,如
8. 作为函数返回值
- 指针:
- 可以返回指针,返回指针的函数需要确保返回的指针不是指向局部变量,因为局部变量在函数结束时会被销毁。
int* createInt() { int* ptr = new int(10); // 动态分配内存 return ptr; }- 调用方需要负责释放返回指针所指向的内存,否则会导致内存泄漏。
int* result = createInt(); delete result; // 释放内存 - 引用:
- 可以返回引用,但要确保返回的引用绑定到不会在函数结束时销毁的对象,通常返回成员变量或全局变量的引用。
int& getRef() { static int var = 10; // 静态变量 return var; }
9. 类型检查
- 指针:
- 不同类型的指针可以进行强制转换,但可能导致未定义行为,如将
int*转换为float*。
int var = 10; float* ptr = (float*)&var; // 强制类型转换,可能导致问题 - 不同类型的指针可以进行强制转换,但可能导致未定义行为,如将
- 引用:
- 引用在类型转换上更加严格,不能将
int&直接转换为float&,需要使用const_cast等类型转换运算符,且要满足一定条件。
int var = 10; const int& ref = var; int& newRef = const_cast<int&>(ref); // 去除 const 性质,需要谨慎使用 - 引用在类型转换上更加严格,不能将
10. 数组和引用
- 指针:
- 数组名在很多情况下会退化为指针,如作为函数参数时。
void printArray(int arr[], int size) { for (int i = 0; i < size; ++i) { std::cout << *(arr + i) << " "; // 数组名退化为指针 } } int arr[] = {1, 2, 3}; printArray(arr, 3); - 引用:
- 可以有数组的引用,保持数组的特性。
void printArray(int (&arr)[3]) { // 引用数组,长度为 3 的数组的引用 for (int i = 0; i < 3; ++i) { std::cout << arr[i] << " "; } } int arr[] = {1, 2, 3}; printArray(arr);
综上所述,引用通常用于避免复制开销和实现函数参数传递时的修改,它更简洁且安全,避免了空指针问题;而指针提供了更多的灵活性,可用于动态内存管理、多级间接引用和重新指向不同对象,但使用时需要更小心,防止空指针和内存泄漏等问题。根据不同的场景和需求,选择使用指针或引用。

1638

被折叠的 条评论
为什么被折叠?



