指针是c/c++中必不可少的一种特殊的变量,基本任何场景都可以使用它,因此学好它是每一个程序员的必经之路,为了初学者可以方便的学习或者复习有关指针的基础知识,我将从零开始将它大多以代码的形式呈现出来,方便大家验证和使用。

一、指针基础入门

1.1 什么是指针?

指针是一种特殊的变量,它存储的不是数据本身,而是数据的内存地址。通过指针,我们可以间接访问和操作内存中的数据。

#include <stdio.h>
int main() 
{
    int number = 42;        // 定义整型变量
    int *pointer = &number; // 定义指针并赋值为number的地址
    printf("变量值: %d\n", number);
    printf("变量地址: %p\n", &number);
    printf("指针值(存储的地址): %p\n", pointer);
    printf("通过指针访问的值: %d\n", *pointer);
    return 0;
}

1.2 指针的基本操作

#include <stdio.h>
int main() 
{
    int x = 10, y = 20;
    int *p1 = &x, *p2 = &y;
    printf("x的值: %d\n", *p1); // 解引用操作,读取指针指向的地址保存的数据
    *p1 = 100; // 通过指针修改变量值
    printf("修改后x的值: %d\n", x);
    p1 = p2; // 指针赋值
    printf("现在p1指向的值: %d\n", *p1);
    return 0;
}

二、指针与数组的深度结合

2.1 数组名的秘密

在C语言中,数组名实际上是一个指向数组首元素的常量指针。

#include <stdio.h>
int main() 
{
    int numbers[5] = {1, 2, 3, 4, 5};
    printf("数组访问方式对比:\n");
    for(int i = 0; i < 5; i++) // 1. 传统下标法
    { 
        printf("numbers[%d] = %d\n", i, numbers[i]);
    }
    int *ptr = numbers;
    for(int i = 0; i < 5; i++)// 2. 指针算术法
    { 
        printf("*(ptr + %d) = %d\n", i, *(ptr + i));
    }
    int *p = numbers;
    for(int i = 0; i < 5; i++) // 3. 指针移动法
    { 
         printf("*p = %d (地址: %p)\n", *p, p);
        p++;
    }
    return 0;
}

2.2 指针数组 vs 数组指针

这是两个容易混淆但完全不同的概念:

#include <stdio.h>
int main() 
{
    int a = 1, b = 2, c = 3;
    int *ptr_array[3] = {&a, &b, &c}; // 指针数组:存储指针的数组
    printf("指针数组:\n");
    for(int i = 0; i < 3; i++) 
    {
        printf("元素%d: %d\n", i, *ptr_array[i]);
    }
    int arr[3] = {10, 20, 30};
    int (*array_ptr)[3] = &arr; // 数组指针:指向数组的指针
    printf("\n数组指针:\n");
    for(int i = 0; i < 3; i++) 
    {
        printf("元素%d: %d\n", i, (*array_ptr)[i]);
    }
    return 0;
}

三、指针与结构体的完美结合

3.1 指向结构体的指针

#include <stdio.h>
#include <string.h>
struct Student  // 定义学生结构体
{
    int id;
    char name[20];
    float score;
};
int main() 
{
    struct Student stu = {101, "张三", 95.5};
    struct Student *stu_ptr = &stu;
    printf("学生信息:\n");
    printf("学号: %d\n", stu_ptr->id); // 使用->操作符
    printf("姓名: %s\n", stu_ptr->name);
    printf("成绩: %.2f\n", stu_ptr->score);
    stu_ptr->score = 98.5; // 通过指针修改结构体成员
    printf("修改后成绩: %.2f\n", stu.score);
    return 0;
}

3.2 结构体中的指针成员

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Person 
{
    char *name;  // 指针成员
    int age;
};
int main() 
{
    struct Person p;
    p.name = (char*)malloc(20 * sizeof(char)); // 为指针成员动态分配内存
    if(p.name == NULL) 
    {
        printf("内存分配失败!\n");
        return 1;
    }
    strcpy(p.name, "李四");
    p.age = 25;
    printf("姓名: %s, 年龄: %d\n", p.name, p.age);
    free(p.name); // 释放动态分配的内存
    return 0;
}

四、多级指针的应用

#include <stdio.h>
int main() 
{
    int value = 100;
    int *ptr = &value;
    int **pptr = &ptr;   // 指向指针的指针
    int ***ppptr = &pptr; // 三级指针
    printf("变量值: %d\n", value);
    printf("一级指针访问: %d\n", *ptr);
    printf("二级指针访问: %d\n", **pptr);
    printf("三级指针访问: %d\n", ***ppptr);
    ***ppptr = 200; // 通过多级指针修改变量值
    printf("修改后的值: %d\n", value);
    return 0;
}

五、指针与动态内存管理

#include <stdio.h>
#include <stdlib.h>
int main() 
{
    int size;
    printf("请输入数组大小: ");
    scanf("%d", &size);
    int *dynamic_array = (int*)malloc(size * sizeof(int)); // 动态分配内存
    if(dynamic_array == NULL) 
    {
        printf("内存分配失败!\n");
        return 1;
    }
    for(int i = 0; i < size; i++)  // 初始化数组
    {
        dynamic_array[i] = i * 10;
    }
    printf("动态数组内容:\n"); // 使用数组
    for(int i = 0; i < size; i++) 
    {
        printf("%d ", dynamic_array[i]);
    }
    printf("\n");
    free(dynamic_array); // 释放内存
    return 0;
}

六、指针函数详解

6.1 返回指针的函数

指针函数是返回指针类型的函数,这类函数在C语言中非常常见,特别是在动态内存分配和数据结构操作中。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* create_copy(const char* original) // 返回指针的函数:创建一个字符串副本
{
    if(original == NULL) return NULL; // 检查输入有效性
    size_t length = strlen(original);
    char* copy = (char*)malloc(length + 1); // 分配内存
    if(copy != NULL) 
    {
        strcpy(copy, original); // 复制内容
    }
    return copy; // 返回指针
}
int main() 
{
    char* my_copy = create_copy("Hello, World!");
    if(my_copy != NULL) 
    {
        printf("副本内容: %s\n", my_copy);
        free(my_copy); // 必须释放内存
    }
    return 0;
}

6.2 函数指针

函数指针是指向函数的指针变量,它允许我们将函数作为参数传递或存储在数据结构中。

#include <stdio.h>
int add(int a, int b) 
{
    return a + b;
}
int subtract(int a, int b) 
{
    return a - b;
}
int multiply(int a, int b) 
{
    return a * b;
}
int main() 
{
    int (*operation)(int, int); // 声明函数指针
    int x = 10, y = 5;
    operation = add; // 使用函数指针执行加法
    printf("%d + %d = %d\n", x, y, operation(x, y));
    operation = subtract;// 使用函数指针执行减法
    printf("%d - %d = %d\n", x, y, operation(x, y));
    operation = multiply;// 使用函数指针执行乘法
    printf("%d * %d = %d\n", x, y, operation(x, y));
    return 0;
}

6.3 函数指针数组

函数指针数组允许我们通过索引调用不同的函数,这在实现状态机或命令模式时非常有用。

#include <stdio.h>
void say_hello() 
{
    printf("Hello!\n");
}
void say_goodbye() 
{
    printf("Goodbye!\n");
}
void say_thanks() 
{
    printf("Thank you!\n");
}
int main() 
{
    void (*messages[3])() = {say_hello, say_goodbye, say_thanks};// 函数指针数组
    for(int i = 0; i < 3; i++) // 通过索引调用不同的函数
    {
        printf("调用函数%d: ", i);
        messages[i]();
    }
    return 0;
}

6.4 回调函数

回调函数是通过函数指针实现的,允许我们将函数作为参数传递给其他函数。

#include <stdio.h>
typedef void (*Callback)(int);// 回调函数类型定义
void process_numbers(int arr[], int size, Callback callback) // 接受回调函数作为参数的函数
{
    for(int i = 0; i < size; i++) 
    {
        callback(arr[i]); // 对每个元素调用回调函数
    }
}
void print_number(int num) // 两个不同的回调函数实现
{
    printf("%d ", num);
}
void print_square(int num) 
{
    printf("%d ", num * num);
}
int main() 
{
    int numbers[] = {1, 2, 3, 4, 5};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    printf("原始数组: ");
    process_numbers(numbers, size, print_number);
    printf("\n平方数组: ");
    process_numbers(numbers, size, print_square);
    printf("\n");
    return 0;
}

七、常见错误与调试技巧

7.1 常见指针错误

#include <stdio.h>
int main() 
{
    int *dangerous_ptr; //未初始化的指针
    // printf("%d", *dangerous_ptr); // 未定义指向
    int *null_ptr = NULL; //空指针解引用,错误
    // printf("%d", *null_ptr); 
    int *wild_ptr; // 使用野指针,错误
    {
        int temp = 50;
        wild_ptr = &temp;
    }
    // printf("%d", *wild_ptr); // 危险访问
    return 0;
}

7.2 调试技巧

  1. 打印指针值和地址:printf("指针地址: %p, 指向的值: %d\n", ptr, *ptr);

    printf("指针地址: %p, 指向的值: %d\n", ptr, *ptr);
  2. 使用调试器

    Visual Studio Debugger (Windows)
  3. 添加断言检查
    #include <assert.h>
    assert(ptr != NULL && "指针不能为NULL");

    最后请大家记住每次使用指针时都要问自己三个问题:1、这个指针初始化了吗?2、这个指针可能为NULL吗?3、我们需要释放这个指针指向的内存吗?

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐