指针基础

  1. 定义和使用
    指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其进行声明。
    例如:

    1
    2
    3
    4
    5
    int  var = 20;   // 实际变量的声明
    int *ip; // 指针变量的声明

    ip = &var; // 在指针变量中存储 var 的地址。&取地址。

  2. 空指针

    1
    int *ptr = NULL;  //ptr的值是 0,避免野指针,确保指针在使用前初始化。
  3. 指针的算数运算
    指针是一个用数值表示的地址。因此,可以对指针进行四种算术运算:++、--、+、-

    • 递增或递减:在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将给出它们之间的元素数量。
  4. 指针的比较运算
    两个指针可以比较,以确定它们是否指向相同的内存位置。例如,如果ptr1和ptr2是指向相同类型的指针,并且它们指向相同的内存位置,那么ptr1 == ptr2将为真。

指针与数组

指针和数组是密切相关的,很多情况下,指针和数组在是可以互换的。
例如,一个指向数组开头(数组第一个元素)的指针,可以通过使用指针的算术运算或数组索引来访问数组。
例如

1
2
3
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // 指向数组的第一个元素
*(p + 2); // 获取arr[2]

指针与字符串

字符串可以通过字符指针表示:

1
2
3
char str[] = "Hello";
char *p = str; // 指向字符串首地址
std::cout << *(p + 1); // 输出e

指针数组

指针数组是一个数组,其每个元素都是一个指针。常用于管理多个动态分配的对象或数据。
例如:

1
int *arr[5]; // 声明一个指针数组,包含5个int类型的指针。即arr是一个数组,数组中的每个元素都是int *类型的指针。

动态内存管理

通过指针动态分配和释放内存:

1
2
int *p = new int(42); // 动态分配一个int类型的内存空间,并将值初始化为42,并将地址赋值给指针p
delete p; // 释放p所指向的内存空间

动态数组:
1
2
int *arr = new int[n]; // 动态分配一个大小为n的int类型的数组,并将地址赋值给指针arr
delete[] arr; // 释放arr所指向的数组内存空间

使用 new 和 delete 动态分配和释放对象:
1
2
3
4
5
6
7
8
9
10
11
12
class 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
6
int 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
6
void increment(int* p) {
(*p)++;
}
int a = 10;
increment(&a);
std::cout << a; // 输出11

指针与结构体

指针可以用于指向结构体,从而方便地访问结构体的成员。
例如:

1
2
3
4
5
6
7
8
struct 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
12
class 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
      #include <iostream>
      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
3
std::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
12
FILE *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
2
int* reg = reinterpret_cast<int*>(0x40021000); // 假设一个硬件寄存器地址
*reg = 0x1; // 设置寄存器值