C++ 中的指针是一个存储内存地址的变量。指针可以指向任何数据类型,包括基本类型、对象、甚至函数。以下是关于指针的核心知识点:
1. 指针的声明和初始化
指针声明时使用 *
表示它是一个指针类型。指针变量保存的是另一个变量的内存地址。
在这段代码中:
a
是一个整型变量,值为10
。p
是一个指向整型变量的指针,它保存的是变量a
的地址。
&a
是取变量 a
的地址。p
保存了这个地址,并通过 *p
可以访问 a
的值。
2. 解引用(Dereferencing)
使用解引用操作符 *
可以访问指针所指向的变量的值:
这里的 *p
访问的是 p
指向的 a
的值,即 10
。
3. 空指针(Null Pointer)
指针可以指向 nullptr
,表示它不指向任何有效的内存位置。未初始化的指针可能指向随机的内存地址,使用这些指针会导致未定义行为。因此,通常将未初始化的指针设置为 nullptr
。
int* ptr = nullptr;
4. 指针的运算
指针可以进行算术运算,特别是在数组的上下文中。对指针进行加减操作时,会根据指针指向的数据类型进行调整。
int arr[] = {1, 2, 3, 4};
int* p = arr; // 指向数组的首地址
p++; // 地址偏移, p 指向下一个元素,即 arr[1]
5. 指针和数组
数组的名称其实就是指向第一个元素的指针。因此,你可以通过指针来访问数组元素。
int arr[] = {1, 2, 3};
int* p = arr;
cout << *p; // 输出 1
cout << *(p+1); // 输出 2
6. 指针与函数
指针可以作为函数的参数,将参数通过指针传递允许函数修改调用者的变量。
void modify(int* p) {
*p = 20;
}
int main() {
int x = 10;
modify(&x); // 传递 x 的地址
cout << x; // 输出 20
return 0;
}
7. 常量指针和指向常量的指针
C++ 提供了两种指针常量:
- 指向常量的指针 (
const T* p
):指针可以指向不同的地址,但不能修改指向的值。 - 常量指针 (
T* const p
):指针本身是常量,不能改变它指向的地址,但可以修改地址中的值。const int* p1; // p1 不能修改指向的值,但可以指向其他地址
int* const p2 = &a; // p2 不能指向其他地址,但可以修改值。8. 指针的注意事项
- 野指针:未初始化或已经释放的指针指向非法内存区域,使用它会导致未定义行为。使用前应确保指针有效,必要时将其设置为
nullptr
。 - 内存泄漏:如果使用
new
动态分配内存,一定要使用delete
释放内存,否则会导致内存泄漏。
int* p = new int(10); // 动态分配
delete p; // 释放内存
p = nullptr; // 防止悬空指针
9. 指向指针的指针(Pointer to Pointer)
指针也可以指向另一个指针,称为 "指向指针的指针"。使用时需要多个 *
来访问不同层次的指针。
int a = 10;
int* p = &a; // p 指向 a
int** pp = &p; // pp 指向 p 的地址
cout << **pp; // 输出 10
在这段代码中,pp
是一个二级指针,它保存了一级指针 p
的地址。通过 **pp
可以访问到 a
的值。
10. 动态内存分配
在 C++ 中,使用 new
和 delete
来进行动态内存分配。指针在动态内存分配中扮演了重要角色。
单个变量的动态分配:
int* p = new int; // 分配一个整型空间
*p = 10;
delete p; // 释放内存
数组的动态分配:
int* arr = new int[5]; // 分配整型数组
arr[0] = 1;
arr[1] = 2;
delete[] arr; // 使用 delete[] 释放数组
11. 函数指针
指针不仅可以指向变量,还可以指向函数。函数指针用于保存函数的地址,并可以通过它调用函数。函数指针常用于回调函数和事件处理。
int add(int a, int b) {
return a + b;
}
int (*funcPtr)(int, int) = add; // 定义指向函数的指针
cout << funcPtr(2, 3); // 通过函数指针调用 add,输出 5
12. 指针和类
指针也可以指向类的对象。通过指针,可以访问对象的成员函数和成员变量。
class MyClass {
public:
int data;
void show() { cout << data; }
};
int main() {
MyClass obj;
obj.data = 10;
MyClass* ptr = &obj; // 指针指向对象
ptr->show(); // 通过指针访问成员函数,输出 10
return 0;
}
13. this
指针
每个类的成员函数都有一个隐藏的指针,名为 this
,它指向调用该函数的对象本身。
class MyClass {
public:
int data;
void setData(int data) {
this->data = data; // 使用 this 指针访问当前对象的成员变量
}
};
14. 常量指针和指针常量的结合
你可以组合指向常量的指针和常量指针,创建复杂的指针类型:
const int* const ptr
:指针既不能修改指向的内容,也不能修改它指向的地址。
15. 智能指针(Smart Pointers)
C++11 引入了智能指针,用来管理动态内存分配,避免手动 delete
。标准库提供了三种主要的智能指针:
std::unique_ptr
:独占所有权的智能指针,不能共享指向的对象。
std::unique_ptr<int> ptr = std::make_unique<int>(10);
std::shared_ptr
:可以共享所有权的智能指针,多个指针可以指向同一对象。对象在最后一个 shared_ptr
被销毁时自动释放内存。
std::shared_ptr<int> p1 = std::make_shared<int>(20);
std::shared_ptr<int> p2 = p1; // p1 和 p2 共享所有权
std::weak_ptr
:一种不增加引用计数的智能指针,常与 std::shared_ptr
结合使用,避免循环引用。
std::weak_ptr<int> wp = p1; // wp 不增加 p1 的引用计数
16. 指针与 void*
void*
是一种通用指针类型,能指向任何类型的数据。但使用 void*
进行解引用时,必须先将其强制转换为具体类型的指针。
int a = 10;
void* ptr = &a;
cout << *(int*)ptr; // 需要将 void* 转换为 int*