目录
指针是一个变量,它的值是另一个变量的地址。它通过存储内存地址来间接访问变量。
指针的声明与初始化
指针的声明格式为:
类型 *指针名;
        例如:
int *ptr; // 声明一个指向int类型的指针
        指针的初始化可以通过取地址运算符&来完成:
int var = 10;
int *ptr = &var; // ptr现在指向变量var的地址
        此时,ptr存储的是var的地址,而不是var的值。要访问var的值,可以使用解引用运算符*:
printf("%d\n", *ptr); // 输出10
        指针运算
指针运算包括指针的加法和减法,这些运算在数组遍历和指针偏移中非常有用。
指针的加法和减法
指针加法和减法的本质是以指针所指向数据类型的大小为单位进行的。例如,对于int类型指针,每次加1实际上是加上sizeof(int)个字节。
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr; // ptr指向数组的第一个元素
ptr++; // ptr现在指向数组的第二个元素
printf("%d\n", *ptr); // 输出20
ptr--; // ptr现在指向数组的第一个元素
printf("%d\n", *ptr); // 输出10
        指针的比较
指针还可以进行比较运算,常用于判断指针是否达到数组的末尾。
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr;
while (ptr < arr + 5) {
    printf("%d ", *ptr);
    ptr++;
}
        指针与数组
数组名在很多情况下可以看作是一个指向数组第一个元素的指针。例如:
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr; // 等同于int *ptr = &arr[0];
        这意味着可以使用指针来遍历数组:
for (int i = 0; i < 5; i++) {
    printf("%d ", *(ptr + i)); // 输出数组的每个元素
}
        通过指针访问数组元素
可以使用指针加偏移量的方式访问数组元素:
printf("%d\n", *(arr + 2)); // 输出30,与arr[2]等价
        同样,可以使用下标运算符[]来访问指针所指向的数组元素:
printf("%d\n", ptr[2]); // 输出30,与arr[2]等价
        指针与多维数组
多维数组的指针处理稍微复杂一些。以二维数组为例:
int arr[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};
        声明指向多维数组的指针
指向二维数组的指针的声明方式如下:
int (*ptr)[4] = arr; // ptr是一个指向包含4个int的数组的指针
        访问多维数组元素
可以使用指针偏移来访问多维数组的元素:
printf("%d\n", *(*(ptr + 1) + 2)); // 输出7,相当于arr[1][2]
        这表示:
ptr + 1移动到第二行,即指向arr[1]。*(ptr + 1)解引用得到第二行的数组,即arr[1]。*(ptr + 1) + 2移动到第二行的第三个元素,即arr[1][2]。- 最终,
*(*(ptr + 1) + 2)解引用得到该元素的值,即7。 
指针数组和数组指针
指针数组和数组指针是两个容易混淆的概念。
指针数组
指针数组是一个数组,其元素是指针。例如:
int *arr[3];
int a = 1, b = 2, c = 3;
arr[0] = &a;
arr[1] = &b;
arr[2] = &c;
        例如,可以创建一个字符指针数组来存储多个字符串:
char *names[] = {"Alice", "Bob", "Charlie"};
for (int i = 0; i < 3; i++) {
    printf("%s\n", names[i]);
}
        数组指针
数组指针是一个指向数组的指针。例如:
int *arr[3];
int a = 1, b = 2, c = 3;
arr[0] = &a;
arr[1] = &b;
arr[2] = &c;
        字符指针
字符指针用于指向字符类型数据,常用于处理字符串。
字符串的定义和字符指针
在C语言中,字符串是以'\0'结尾的字符数组。字符指针可以指向字符串的第一个字符:
char str[] = "Hello, world!";
char *ptr = str; // ptr指向字符串的第一个字符
        通过字符指针可以访问字符串中的各个字符:
while (*ptr != '\0') {
    printf("%c", *ptr);
    ptr++;
}
        直接使用字符指针初始化字符串
可以直接使用字符指针指向字符串常量:
char *ptr = "Hello, world!";
        需要注意的是,字符串常量是存储在只读内存中的,不能通过指针修改其内容:
char *ptr = "Hello, world!";
ptr[0] = 'h'; // 未定义行为,可能导致程序崩溃
        void指针
void指针是一种通用指针类型,可以指向任何类型的数据,但不能直接解引用。需要先进行类型转换。
void指针的声明与使用
        void *ptr;
int a = 10;
ptr = &a; // void指针指向int类型的变量
// 需要进行类型转换后才能解引用
printf("%d\n", *(int *)ptr);
        const修饰指针
const可以修饰指针,使指针或指针指向的内容不可修改。
修饰指针指向的内容
int a = 10;
const int *ptr = &a; // ptr指向的内容不可修改
// *ptr = 20; // 错误:尝试修改只读变量
ptr = &b; // 可以修改指针本身
        本身 
修饰指针本身
int a = 10;
int b = 20;
int *const ptr = &a; // ptr本身不可修改
*ptr = 20; // 可以修改指针指向的内容
// ptr = &b; // 错误:尝试修改const指针
        同时修饰指针和指针指向的内容
int a = 10;
const int *const ptr = &a; // ptr本身和ptr指向的内容都不可修改
// *ptr = 20; // 错误:尝试修改只读变量
// ptr = &b; // 错误:尝试修改const指针
        多级指针
多级指针是指指向指针的指针。它们可以用于间接访问变量,常用于处理复杂数据结构。
二级指针
二级指针是指向指针的指针:
int a = 10;
int *ptr = &a;
int **pptr = &ptr; // 二级指针指向一级指针
printf("%d\n", **pptr); // 输出10,通过二级指针间接访问变量a
        多级指针的使用
多级指针在处理动态内存分配和多维数组时非常有用:
int rows = 3, cols = 4;
int **matrix = malloc(rows * sizeof(int *));
for (int i = 0; i < rows; i++) {
    matrix[i] = malloc(cols * sizeof(int));
}
// 使用matrix
for (int i = 0; i < rows; i++) {
    for (int j = 0; j < cols; j++) {
        matrix[i][j] = i * cols + j;
    }
}
// 释放matrix
for (int i = 0; i < rows; i++) {
    free(matrix[i]);
}
free(matrix);
        指向函数的指针
指针不仅可以指向变量,还可以指向函数。函数指针用于实现回调函数和动态函数调用。
int add(int a, int b) {
    return a + b;
}
int (*func_ptr)(int, int) = add; // 声明并初始化函数指针
printf("%d\n", func_ptr(2, 3)); // 输出5,通过函数指针调用函数
        指针函数
指针函数是指返回指针的函数。它在需要返回动态分配的内存块或者其他指针类型数据时非常有用。
示例
int* createArray(int size) {
    int *arr = malloc(size * sizeof(int));
    for (int i = 0; i < size; i++) {
        arr[i] = i;
    }
    return arr;
}
int main() {
    int *array = createArray(5);
    for (int i = 0; i < 5; i++) {
        printf("%d ", array[i]);
    }
    free(array); // 记得释放内存
    return 0;
}
        指针传递
在函数调用时传递指针,可以实现对原数据的修改。这是C语言实现传引用的方式。
示例
void increment(int *num) {
    (*num)++;
}
int main() {
    int a = 5;
    increment(&a);
    printf("%d\n", a); // 输出6
    return 0;
}
        指针与动态内存分配
C语言中使用malloc、calloc和realloc进行动态内存分配,使用free释放内存。
示例
int main() {
    int *arr = malloc(5 * sizeof(int));
    if (arr == NULL) {
        printf("Memory allocation failed");
        return 1;
    }
    for (int i = 0; i < 5; i++) {
        arr[i] = i * i;
    }
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    free(arr); // 释放内存
    return 0;
}
        函数指针
函数指针是指向函数的指针,可以用于实现回调函数和动态函数调用。
示例
int add(int a, int b) {
    return a + b;
}
int subtract(int a, int b) {
    return a - b;
}
int main() {
    int (*operation)(int, int);
    operation = add;
    printf("Add: %d\n", operation(5, 3)); // 输出8
    operation = subtract;
    printf("Subtract: %d\n", operation(5, 3)); // 输出2
    return 0;
}
        回调函数
回调函数是一种通过函数指针调用的函数,常用于事件驱动编程和异步编程。
示例
void processArray(int *arr, int size, void (*process)(int *))