C++ 指针应用
指针基础
定义和使用
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其进行声明。
例如:1
2
3
4
5int var = 20; // 实际变量的声明
int *ip; // 指针变量的声明
ip = &var; // 在指针变量中存储 var 的地址。&取地址。空指针
1
int *ptr = NULL; //ptr的值是 0,避免野指针,确保指针在使用前初始化。
指针的算数运算
指针是一个用数值表示的地址。因此,可以对指针进行四种算术运算:++、--、+、-
。- 递增或递减:在C++中,指针是一个变量,它存储一个内存地址。递增(递减)一个指针意味着将指针指向下一个(上一个)内存位置,这通常是指向下一个(上一个)数组元素。递增(递减)一个指针会根据指针所指向的数据类型自动调整指针的值。例如,若为int型指针,指针 ptr 会向前(向后)移动 4 个字节,指向下一个(上一个)整型元素的地址。
- 加法运算:当一个指针p加上一个整数n时,结果是指针p向前移动n个元素的大小。例如,如果ptr是一个int类型的指针,每个int占4个字节,那么p + 1将指向p所指向的下一个int元素。
- 减法运算:当一个指针p减去一个整数n时,结果是指针p向后移动n个元素的大小。例如,如果ptr是一个int类型的指针,每个int占4个字节,那么p - 1将指向p所指向的上一个int元素。
- 指针与指针之间的减法运算:两个指针相减的结果是它们之间的元素数量。例如,如果ptr1和ptr2是指向相同类型的指针,并且ptr1在ptr2之前,那么ptr1 - ptr2将给出它们之间的元素数量。
- 指针的比较运算
两个指针可以比较,以确定它们是否指向相同的内存位置。例如,如果ptr1和ptr2是指向相同类型的指针,并且它们指向相同的内存位置,那么ptr1 == ptr2将为真。
指针与数组
指针和数组是密切相关的,很多情况下,指针和数组在是可以互换的。
例如,一个指向数组开头(数组第一个元素)的指针,可以通过使用指针的算术运算或数组索引来访问数组。
例如1
2
3int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // 指向数组的第一个元素
*(p + 2); // 获取arr[2]
指针与字符串
字符串可以通过字符指针表示:1
2
3char str[] = "Hello";
char *p = str; // 指向字符串首地址
std::cout << *(p + 1); // 输出e
指针数组
指针数组是一个数组,其每个元素都是一个指针。常用于管理多个动态分配的对象或数据。
例如:1
int *arr[5]; // 声明一个指针数组,包含5个int类型的指针。即arr是一个数组,数组中的每个元素都是int *类型的指针。
动态内存管理
通过指针动态分配和释放内存:1
2int *p = new int(42); // 动态分配一个int类型的内存空间,并将值初始化为42,并将地址赋值给指针p
delete p; // 释放p所指向的内存空间
动态数组:1
2int *arr = new int[n]; // 动态分配一个大小为n的int类型的数组,并将地址赋值给指针arr
delete[] arr; // 释放arr所指向的数组内存空间
使用 new 和 delete 动态分配和释放对象:1
2
3
4
5
6
7
8
9
10
11
12class Box {
public:
int width;
void display() {
std::cout << "Width: " << width << std::endl;
}
};
Box* b = new Box(); // 动态分配
b->width = 20;
b->display();
delete b; // 释放动态分配的对象
函数指针
函数指针是一个指向函数的指针,可以用来调用函数。函数指针可以用于回调函数、事件处理等场景。
例如:1
2
3
4
5
6int add(int a, int b) {
return a + b;
}
int (*p)(int, int) = add; // 声明一个函数指针p,指向add函数
int result = p(2, 3); // 调用add函数,并将结果赋值给result
函数参数传递
将实参的地址传递给形参,函数内部通过解引用指针访问和修改实参。
例如:1
2
3
4
5
6void increment(int* p) {
(*p)++;
}
int a = 10;
increment(&a);
std::cout << a; // 输出11
指针与结构体
指针可以用于指向结构体,从而方便地访问结构体的成员。
例如:1
2
3
4
5
6
7
8struct Person {
std::string name;
int age;
};
struePerson p = {"Tom", 20};
Person *ptr = &p; // 指向结构体p的指针
std::cout << ptr->name; // 输出Tom
指针与类
指针可以用于访问成员函数和变量。
通过箭头操作符(->)访问对象的成员:1
2
3
4
5
6
7
8
9
10
11
12class Box {
public:
int width;
void display() {
std::cout << "Width: " << width << std::endl;
}
};
Box box;
Box *ptr = &box;
ptr->width = 20;
ptr->display(); // 输出Width: 20
指针与多态
多态允许程序通过基类的指针或引用调用派生类的成员函数,从而实现运行时的动态行为。
原理:
- 基类指针指向派生类对象:
- 通过基类指针访问派生类的成员。
- 基类中必须有虚函数,才能实现多态。
- 动态绑定:
- 虚函数的调用在运行时决定,而不是在编译时。
- 动态绑定由虚函数表(vtable)和虚函数指针(vptr)实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using namespace std;
class Shape {
public:
virtual void draw() const { // 虚函数
cout << "Drawing Shape" << endl;
}
virtual ~Shape() {} // 虚析构函数
};
class Circle : public Shape {
public:
void draw() const override { // 重写基类的虚函数
cout << "Drawing Circle" << endl;
}
};
class Rectangle : public Shape {
public:
void draw() const override {
cout << "Drawing Rectangle" << endl;
}
};
int main() {
Shape* shape1 = new Circle(); // 基类指针指向派生类对象
Shape* shape2 = new Rectangle(); // 基类指针指向派生类对象
shape1->draw(); // 动态绑定,调用 Circle 的 draw
shape2->draw(); // 动态绑定,调用 Rectangle 的 draw
delete shape1; // 释放内存
delete shape2;
return 0;
}
// 输出:
//Drawing Circle
//Drawing Rectangle
指针与迭代器
STL中,迭代器本质是指针的抽象:1
2
3std::vector<int> vec = {1, 2, 3};
auto it = vec.begin();
std::cout << *it; // 输出1
指针与文件操作
指针可以用于读取和写入文件,通过文件指针(FILE*)操作文件。
例如:1
2
3
4
5
6
7
8
9
10
11
12FILE *file = fopen("example.txt", "r"); // 打开文件
if (file == NULL) {
std::cout << "Failed to open file" << std::endl;
return 1;
}
char buffer[100];
while (fgets(buffer, sizeof(buffer), file) != NULL) {
std::cout << buffer;
}
fclose(file); // 关闭文件
智能指针
C++11引入智能指针,用来自动管理动态内存,避免内存泄漏和悬挂指针问题。通过标准库提供的模板类,如 std::unique_ptr、std::shared_ptr 和 std::weak_ptr,可以替代原生指针的手动管理,提升代码的安全性和可读性。
- unique_ptr:独占指针,不可复制,但可以移动。
- shared_ptr:共享指针,多个shared_ptr可以指向同一个对象,通过引用计数管理对象的生命周期。
- weak_ptr:弱指针,不增加引用计数,用于避免循环引用。
内存映射与硬件编程
指针直接操作内存地址,用于嵌入式开发和硬件编程:1
2int* reg = reinterpret_cast<int*>(0x40021000); // 假设一个硬件寄存器地址
*reg = 0x1; // 设置寄存器值