[从0开始的C语言] C语言指针知识点总结(1)

目录

1. 内存与地址:指针的基础概念

2. 指针变量和地址:指向“地址卡片”的指针

3. 指针变量类型的意义:指针的类型决定了它的“指向”类型

4. 指针运算:你可以对指针进行加减法

5. const修饰指针:限制指针的行为

6. 野指针:小心使用未初始化的指针

如何避免野指针?

7. assert断言:程序自检的利器

8. 指针的使用和传址调用:借助指针传递地址

9. 数组名的理解:数组即指针

10. 使用指针访问数组:指针的强大功能

11. 进阶指针应用

11.1 二级指针(Pointer to Pointer)

11.2 指针数组模拟二维数组

12. 函数指针(Function Pointers)

13. 回调函数(Callback Functions)


指针是C语言的核心之一,它是编程中一个相对复杂但却非常强大的工具。在本篇博客中,我们将详细回顾指针的基础知识,并通过代码演示帮助大家轻松掌握指针的用法。

1. 内存与地址:指针的基础概念

想象一下,计算机的内存就像一个巨大的仓库,里面有无数的存储单元(也就是内存单元),每个单元都有唯一的编号。这个编号就是内存地址。每次你创建一个变量时,操作系统会在内存中为它分配一个存储位置,并将该位置的地址赋给这个变量。

生活中的例子:

比如,你有一个手机联系人列表,每个联系人都用一个编号(地址)来标识你可以通过编号来查找到具体的联系人信息。在C语言中,变量就像这些联系人,内存地址就像这些编号。

2. 指针变量和地址:指向“地址卡片”的指针

指针是存储地址的变量。它并不直接存储数据本身,而是存储其他变量的内存地址。指针就像是给变量“打上了地址标签”,你可以通过它间接访问或修改变量的值。

#include <stdio.h>

int main() {
    int a = 10;    // 普通变量
    int *p = &a;   // 指针变量,存储a的地址

    printf("a的值:%d\n", a);     // 输出a的值
    printf("a的地址:%p\n", &a); // 输出a的地址
    printf("p指向的值:%d\n", *p); // 输出p指向的值

    return 0;
}

在上面的代码中,p是一个指针变量,它存储了a的地址。通过*p,我们可以解引用指针,获取a的值。

3. 指针变量类型的意义:指针的类型决定了它的“指向”类型

每个指针都有一个特定的类型,它决定了指针所指向的数据类型。例如,int *p表示p是一个指向int类型变量的指针。

#include <stdio.h>

int main() {
    int a = 10;
    int *p = &a; // p是指向整数的指针

    // 解引用指针p,获取a的值
    printf("p指向的值:%d\n", *p); // 输出10

    return 0;
}

如果我们将int *p改为char *p,那么p就会变成一个指向char类型数据的指针。指针类型是非常重要的,它保证我们在解引用时,能够正确地访问数据。

4. 指针运算:你可以对指针进行加减法

指针不仅仅是存储地址,还可以进行“运算”。例如,指针加减一个整数值,指针间的相减等操作。需要注意的是,指针的运算是基于它所指向数据类型的大小来进行的。

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *p = arr;

    printf("p指向的值:%d\n", *p);     // 输出1
    printf("p+1指向的值:%d\n", *(p + 1)); // 输出2

    return 0;
}

在上面的代码中,p + 1会指向数组arr的下一个元素(即arr[1])。因为pint *类型指针,它会自动考虑int类型占用的内存空间(通常是4字节),所以p + 1会指向内存中比p多4字节的地方。

5. const修饰指针:限制指针的行为

C语言中的const关键字可以用来修饰指针,指示指针的某些部分不可修改。我们可以使用const来限制指针指向的内容或指针本身。

#include <stdio.h>

int main() {
    int a = 10;
    const int *p = &a; // p指向的内容不可修改
    // *p = 20; // 错误:不能修改*p指向的内容

    int *const q = &a; // q本身不可修改
    // q = &b; // 错误:不能修改q本身

    return 0;
}
  • const int *p:指针p指向的内容不可修改,但指针p可以重新指向其他变量。
  • int *const p:指针p本身不可修改,但可以通过指针修改p所指向的内容。

6. 野指针:小心使用未初始化的指针

野指针是指指向未知或已释放内存的指针。使用野指针可能会导致程序崩溃或数据错误。

如何避免野指针?

  1. 初始化指针:始终将指针初始化为NULL
  2. 在使用指针之前,先检查它是否为NULL
#include <stdio.h>

int main() {
    int *p = NULL; // 初始化为NULL

    if (p != NULL) {
        // 使用p
    } else {
        printf("p是NULL,不能使用\n");
    }

    return 0;
}

7. assert断言:程序自检的利器

assert是一个非常有用的调试工具。它用于在运行时检查某个条件是否成立。如果条件不成立,程序会中止,并输出错误信息。

#include <stdio.h>
#include <assert.h>

int main() {
    int a = 10;
    assert(a > 0); // 如果a不大于0,程序会终止

    return 0;
}

如果a的值小于或等于0,assert会导致程序崩溃,并给出错误信息。

使用 assert 来判断指针是否为空是一种简单的调试方法。在开发过程中,可以通过断言确保指针在使用之前不为空。如果指针为空,程序会被中断,并显示相关的错误信息。

#include <stdio.h>
#include <assert.h>

void process_pointer(int *ptr) {
    // 使用 assert 判断指针是否为空
    assert(ptr != NULL);  // 如果 ptr 是空指针,程序会终止并输出错误信息

    // 如果 assert 通过,执行相关操作
    printf("Pointer is not NULL, processing data: %d\n", *ptr);
}

int main() {
    int *ptr = NULL;  // 空指针

    // 调用函数,assert 会检查 ptr 是否为空
    process_pointer(ptr);

    return 0;
}

8. 指针的使用和传址调用:借助指针传递地址

指针常用于传递地址,这使得函数能够直接修改传入变量的值(即“传址调用”)。通过指针,函数能够访问到原始数据。

#include <stdio.h>

void increment(int *p) {
    (*p)++; // 增加指针指向的变量的值
}

int main() {
    int a = 10;
    increment(&a);  // 传递a的地址
    printf("a的值:%d\n", a); // 输出11

    return 0;
}

在上面的例子中,increment函数通过指针p修改了a的值。传递地址的好处是避免了函数返回值的问题,并且可以直接修改原始数据。

9. 数组名的理解:数组即指针

在C语言中,数组名实际上是指向数组首元素的指针。也就是说,arr实际上是&arr[0]的别名。你可以通过数组名直接访问数组的元素。

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *p = arr;

    printf("arr[0]的值:%d\n", *p); // 输出1

    return 0;
}

在这个例子中,arr就是指向数组首元素arr[0]的指针。

10. 使用指针访问数组:指针的强大功能

通过指针,我们不仅可以访问数组元素,还可以进行更灵活的操作。

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *p = arr;

    for (int i = 0; i < 5; i++) {
        printf("arr[%d]的值:%d\n", i, *(p + i));
    }

    return 0;
}

在这个例子中,p + i表示指向arr[i]的指针,通过*(p + i)访问数组的每一个元素。

11. 进阶指针应用

11.1 二级指针(Pointer to Pointer)

二级指针(**ptr)是指向指针的指针,它用于管理动态二维数组或者在函数中修改指针变量的值。

#include <stdio.h>

int main() {
    int num = 10;
    int *ptr = &num;  // ptr 是一个指向 num 的指针
    int **ptr2 = &ptr; // ptr2 是一个指向 ptr 的指针

    // 访问 num
    printf("Value of num: %d\n", num);
    printf("Value using ptr: %d\n", *ptr);
    printf("Value using ptr2: %d\n", **ptr2);

    return 0;
}
  • ptr 是一个指向 int 的指针,指向 num
  • ptr2 是一个指向 ptr 的指针,因此 **ptr2 就是 num 的值。

11.2 指针数组模拟二维数组

指针数组是一个数组,其中每个元素都是一个指针。可以使用指针数组来模拟二维数组。

#include <stdio.h>

int main() {
    // 创建一个二维数组
    int arr[3][3] = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };

    // 使用指针数组模拟二维数组
    int *ptrArr[3];  // ptrArr 是一个数组,包含 3 个指向 int 的指针
    for (int i = 0; i < 3; i++) {
        ptrArr[i] = arr[i];  // 将每行的指针赋给 ptrArr
    }

    // 通过指针数组访问二维数组
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            printf("%d ", *(ptrArr[i] + j));  // 通过指针偏移访问元素
        }
        printf("\n");
    }

    return 0;
}
  • ptrArr 是一个包含 3 个元素的数组,每个元素都是一个指向 int 的指针,指向二维数组的每一行。
  • 通过 *(ptrArr[i] + j) 访问每个元素,相当于使用二维数组的下标。

12. 函数指针(Function Pointers)

函数指针是指向函数的指针,它允许你在运行时动态地选择调用哪个函数。函数指针在回调函数和事件处理等场景中非常有用。

#include <stdio.h>

// 定义一个函数类型
typedef void (*func_ptr)(int);

void print_number(int n) {
    printf("Number: %d\n", n);
}

int main() {
    // 定义一个函数指针并指向函数 print_number
    func_ptr ptr = print_number;

    // 通过函数指针调用函数
    ptr(5);

    return 0;
}
  • typedef void (*func_ptr)(int) 定义了一个函数指针类型 func_ptr,它指向返回类型为 void 且接受一个 int 类型参数的函数。
  • ptr 是一个函数指针,指向 print_number 函数。通过 ptr(5) 调用该函数。

13. 回调函数(Callback Functions)

回调函数是通过函数指针传递给另一个函数的函数。回调函数常用于事件驱动编程或需要动态调用的场景。我们将在此例中演示一个简单的回调函数示例。

#include <stdio.h>

// 回调函数类型定义
typedef void (*callback_t)(int);

// 计算并回调的函数
void calculate(int x, int y, callback_t callback) {
    int result = x + y;
    callback(result);  // 调用回调函数
}

// 回调函数实现
void print_result(int result) {
    printf("Result is: %d\n", result);
}

int main() {
    // 使用回调函数
    calculate(5, 3, print_result);

    return 0;
}
  • calculate 函数接受两个整数和一个回调函数指针作为参数。
  • callback_t 是一个指向接受 int 类型参数并返回 void 的函数的指针类型。
  • calculate 函数计算 x + y 的结果,并调用传入的回调函数 print_result 来打印结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值