C 语言指针

C语言中的内存

静态内存(Static Memory)

  • Global variables, accessible throughout the whole program.
  • Defined with static keywork, as well as variables defined in global scope.

栈内存(Stack Memory)

  • Local variables with functions. Destroyed after function exits.

堆内存(Heap Memory)

  • You control creation and destruction of these variables: malloc(), free()
  • Can lead to memory leaks, use-after-free issues.

指针 (变量)= 内存地址

定义指针的 * : 标记

定义格式 :

	数据类型 * 变量名
    int a = 10;
    int *p1 = &a;

    double b = 10;
    double *p2 = &b;

指针的作用

指针是一个 64 位整数,其值为内存中的地址。

查询数据

* : 解引用运算符

    int a = 10;
    int *p1 = &a;
    printf("%d\n", *p1); // 10
  • 指针变量的数据类型要和指向变量的类型保持一致

  • 指针变量占用的大小,跟数据类型无关,跟编译器有关(32位 : 4字节;64位:8字节)

  • 给指针变量赋值的时候,不能把一个数赋值给指针变量

    int a = 10;
    int *p1 = &a;
	// int *p1 = 100; 错误,将100看成内存地址
    printf("%d\n", *p1); // 10
    *p1 = 20;
    printf("%d\n", *p1); // 20

操作其他函数中的变量

#include <stdlib.h>
#include <stdio.h>

void swap(int *p1, int *p2)
{
    int tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
}

int main(int argc, char const *argv[])
{
    int a = 10, b = 20;
    printf("%d, %d\n", a, b);
    swap(&a, &b);
    printf("%d, %d\n", a, b);
    return 0;
}

函数返回多个值

#include <stdio.h>

void getMaxAndMin(int arr[], int len, int *max, int *min)
{
    *max = arr[0];
    *min = arr[0];
    for (int i = 1; i < len; i++)
    {
        if (arr[i] > *max)
        {
            *max = arr[i];
        }
    }
    for (int i = 1; i < len; i++)
    {
        if (arr[i] < *min)
        {
            *min = arr[i];
        }
    }
}

int main(int argc, char const *argv[])
{
    int arr[] = {1, 2, 3, 100, 4, 5, 6, 7, 8};
    int len = sizeof(arr) / sizeof(arr[0]);
    int max = arr[0];
    int min = arr[0];
    getMaxAndMin(arr, len, &max, &min);
    printf("Max: %d\n", max);
    printf("Min: %d\n", min);
    return 0;
}

函数的结果和计算状态分开

#include <stdio.h>

int getRemainder(int num1, int num2, int *remainder)
{
    if (num2 == 0)
    {
        return 1;
    }
    *remainder = num1 % num2;
    return 0;
}

int main(int argc, char const *argv[])
{
    int a = 10;
    int b = 2;
    int res = 0;

    int flag = getRemainder(a, b, &res);

    if (!flag)
    {
        printf("Quotient: %d\n", res);
    }

    return 0;
}

方便的操作数组和函数

#include <stdio.h>

int main(int argc, char const *argv[])
{
    /*
        指针运算
            步长:指针移动一次,走的字节数目
                char : 1
                short : 2
                int : 4
                long : 4
                long long : 8
                加法/减法
     */
    int a = 10;
    int *p = &a;
    printf("p = %p, *p = %d\n", p, *p);
    printf("%p\n", p + 1);
    printf("%p\n", p - 1);
    printf("%p\n", p + 2);
    long x = 10;
    int x = 10;
    return 0;
}
#include <stdio.h>

int main(int argc, char const *argv[])
{
    /*
        指针运算
            步长:指针移动一次,走的字节数目
                char : 1
                short : 2
                int : 4
                long : 4
                long long : 8
                加法/减法
     */
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int *p1 = &arr[0];

    printf("p1 指向的元素: %d\n", *p1);
    printf("p1 + 1 指向的元素: %d\n", *(p1 + 1));

    int *p2 = &arr[5];
    printf("%d\n", p1);
    printf("%d\n", p2);

    printf("%d\n", p2 - p1);

    return 0;
}

指向不明的指针

野指针:指针指向的空间未分配

悬空指针:指针指向的空间已经分配,但是被释放了

#include <stdio.h>

int *method()
{
    int num = 13;
    int *p = &num;
    return p;
}

int main(int argc, char const *argv[])
{
    int a = 10;
    int *p1 = &a;
    printf("%p\n", p1);
    printf("%d\n", *p1);

    // p2 野指针
    int *p2 = p1 + 10;
    printf("%p\n", p2);
    printf("%d\n", *p2);

    // 悬空指针:指针指向的内存已分配,但是被释放了

    int *p3 = method();
    printf("delay some time\n");

    printf("%p\n", p3);
    printf("%d\n", *p3);
    return 0;
}

没有类型的指针

特点:无法获取数据,无法计算,但是可以接受任意地址

#include <stdio.h>

// swap 函数用于交换两个内存区域的内容
// p1 和 p2 是指向要交换的内存地址的指针,len 是要交换的字节数
void swap(void *p1, void *p2, int len)
{
    // 将 void 指针转换为 char 指针,便于字节级别的操作
    char *pc1 = p1;
    char *pc2 = p2;

    char temp = 0; // 临时变量,用于存储字节内容

    // 循环 len 次,逐字节交换内容
    for (int i = 0; i < len; i++)
    {
        temp = *pc1;   // 保存 pc1 指向的字节
        *pc1 = *pc2;   // 将 pc2 指向的字节赋值给 pc1
        *pc2 = temp;   // 将原 pc1 指向的字节赋值给 pc2

        pc1++; // 移动到下一个字节
        pc2++; // 移动到下一个字节
    }
}

int main(int argc, char const *argv[])
{
    int a = 10; // 整数变量 a
    short b = 20; // 短整型变量 b
    int *p1 = &a; // 指向 a 的指针
    short *p2 = &b; // 指向 b 的指针

    // 打印变量 a 和 b 的值
    printf("a = %d, b = %d\n", a, b);
    printf("*p1 = %d, *p2 = %d\n", *p1, *p2);

    // 不同类型的指针之间不能直接赋值
    // void 指针可以接受任何类型的指针
    char *p3 = (char *)p1; // 将整型指针 p1 转换为字符型指针
    // printf("%c\n", *p3); // 不建议解引用,因为 p3 可能指向非字符数据

    void *p4 = p1; // 使用 void 指针 p4 存储 p1 的地址
    printf("%p\n", p4); // 打印 p4 的地址
    printf("%p\n", p4 + 1); // 虽然可以进行指针算术运算,但这里的结果不安全

    void *p5 = p2; // 使用 void 指针 p5 存储 p2 的地址
    // printf("%d\n", *p5); // 不建议解引用,因为 p5 是 void 指针

    // 使用 swap 函数交换两个 long long int 类型的值
    long long int c = 100; // 声明 long long int 变量 c
    long long int d = 200; // 声明 long long int 变量 d
    printf("%lld,%lld\n", c, d); // 打印 c 和 d 的值

    swap(&c, &d, sizeof(long long int)); // 调用 swap 函数进行交换
    printf("%lld,%lld\n", c, d); // 打印交换后的 c 和 d 的值

    return 0;
}

二级指针和多级指针

指针数据类型:跟指向空间中,数据的类型是保持一致的。

作用:二级指针可以操作一级指针记录的地址

#include <stdio.h>

int main(int argc, char const *argv[])
{

    int a = 10;
    int b = 20;

    int *p = &a;
    int **pp = &p;

    // 作用一:利用二级指针修改一级指针里面记录的内存地址
    *pp = &b;

    printf("%p\n", &a);
    printf("%p\n", &b);
    printf("%p\n", p);
    // 作用二:利用二级指针获取变量中记录的数据
    printf("%d\n", **pp);

    return 0;
}

数组指针的基本用法

概念:指向数组的指针,叫做数组指针

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int arr[] = {10, 20, 30, 40, 50};
    int len = sizeof(arr) / sizeof(int);

    int *p1 = arr;
    int *p2 = &arr[0];

    printf("%p\n", p1);
    printf("%p\n", p2);

    printf("%d\n", *p1);
    printf("%d\n", *(p1 + 1));
    printf("%d\n", *(p1 + 2));
    printf("%d\n", *(p1 + 3));

    for (int i = 0; i < len; i++)
    {
        printf("%d\n", *p1++);
    }

    return 0;
}
  • arr参与计算的时候,会退化为第一个元素的指针
    • 特殊情况:
      • sizeof 运算的时候,不会退化,arr还是整体
      • &arr获取地址的时候,不会退化
#include <stdio.h>

int main(int argc, char const *argv[])
{
    int arr[] = {10, 20, 30, 40, 50};
    int len = sizeof(arr) / sizeof(int);

    // sizeof 运算的时候,arr还是整体
    printf("%zu\n", sizeof(arr));

    printf("%p\n", arr);
    // &arr 时, arr还是整体
    printf("%p\n", &arr);

    printf("%p\n", arr + 1);
    printf("%p\n", &arr + 1);

    return 0;
}

通过指针便利数组

#include <stdio.h>

int main(int argc, char const *argv[])
{
    /*
        二维数组的定义和遍历方式

        定义格式方式一:
        int arr[行数][列数] = {
            {元素1, 元素2, ...},
            {元素1, 元素2, ...},
            ...
        };
    */

    // 定义一个 3x5 的二维数组并初始化
    int arr[3][5] = {
        {1, 2, 3, 4, 5}, // 第一行
        {1, 2, 3, 4, 5}, // 第二行
        {1, 2, 3, 4, 5}  // 第三行
    };

    // 定义一个指向包含 5 个整数的数组的指针,指向 arr
    int(*p)[5] = arr;

    // 遍历二维数组
    for (int i = 0; i < 3; i++) // 外层循环遍历行
    {
        for (int j = 0; j < 5; j++) // 内层循环遍历列
        {
            // 使用指针解引用和偏移来访问数组元素
            printf("%d ", *(*p + j)); // 打印当前行的元素
        }
        printf("\n"); // 换行以分隔不同的行
        p += 1;       // 移动指针到下一行
    }

    return 0; // 返回 0 表示程序正常结束
}
#include <stdio.h>

int main(int argc, char const *argv[])
{
    /*
        二维数组的定义和遍历方式

        定义格式方式二:
        通过一维数组的指针数组来实现二维数组的效果。
    */

    // 定义三个一维数组
    int arr1[3] = {1, 2, 3};                   // 第一个数组,包含 3 个元素
    int arr2[5] = {1, 2, 3, 4, 5};             // 第二个数组,包含 5 个元素
    int arr3[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; // 第三个数组,包含 9 个元素

    // 计算每个数组的长度
    int len1 = sizeof(arr1) / sizeof(int); // arr1 的长度
    int len2 = sizeof(arr2) / sizeof(int); // arr2 的长度
    int len3 = sizeof(arr3) / sizeof(int); // arr3 的长度

    // 将数组长度存入一个一维数组
    int len[3] = {len1, len2, len3}; // 存储各数组的长度

    // 定义一个指针数组,指向三个一维数组
    int *arr[3] = {arr1, arr2, arr3}; // arr 数组中的每个元素都是指向整型的指针

    // 定义一个指向指针数组的指针
    int **p = arr; // p 是指向指向整型的指针的指针

    // 遍历指针数组
    for (int i = 0; i < 3; i++) // 外层循环遍历指针数组的行
    {
        for (int j = 0; j < len[i]; j++) // 内层循环遍历当前数组的列
        {
            // 使用指针解引用,打印当前数组的元素
            printf("%d ", *(*p + j)); // 打印当前行的第 j 个元素
        }
        printf("\n"); // 换行以分隔不同的数组
        p += 1;       // 移动指针到下一个数组
    }

    return 0; // 返回 0 表示程序正常结束
}

数组指针和指针数组

数组指针:指向数组的指针

作用:方便的操作数组中的各种数据

举例:int* p = arr; (步长为:int 4个字节)

举例:int (*p)[5] = &arr; (步长为 :int 乘5 20个字节)

指针数组:存放指针的数组

作用:用来存放指针

举例:int *p[5], 这个数组里面存放着int类型的指针

函数和指针

格式:返回值类型(*指针名)(形参列表)

作用:利用函数指针,可以动态的调用函数

#include <stdio.h>

// 定义一个无参数无返回值的函数
void method1()
{
    printf("method1\n"); // 打印函数名
}

// 定义一个接受两个整数并返回整数的函数
int method2(int num1, int num2)
{
    printf("method2\n"); // 打印函数名
    return num1 + num2;  // 返回两个参数的和
}

int main(int argc, char const *argv[])
{
    // 声明一个指向无参数无返回值函数的指针
    void (*p1)() = method1; // p1 指向 method1 函数

    // 声明一个指向接受两个整数并返回整数的函数的指针
    int (*p2)(int, int) = method2; // p2 指向 method2 函数

    // 调用无参数函数
    p1(); // 通过指针调用 method1

    // 调用有参数的函数,并存储返回值
    int num = p2(1, 2);  // 通过指针调用 method2,传递参数 1 和 2
    printf("%d\n", num); // 打印 method2 的返回值

    return 0; // 返回 0 表示程序正常结束
}

函数指针

#include <stdio.h>

// 定义加法函数
int add(int num1, int num2)
{
    return num1 + num2; // 返回两个数的和
}

// 定义减法函数
int subtract(int num1, int num2)
{
    return num1 - num2; // 返回第一个数减去第二个数的结果
}

// 定义乘法函数
int multiply(int num1, int num2)
{
    return num1 * num2; // 返回两个数的积
}

// 定义除法函数
int divide(int num1, int num2)
{
    return num1 / num2; // 返回第一个数除以第二个数的结果
}

int main(int argc, char const *argv[])
{
    int a = 10; // 示例变量 a
    int b = 20; // 示例变量 b

    // 创建一个函数指针数组,存储四个基本运算函数
    int (*arr[4])(int, int) = {add, subtract, multiply, divide};

    // 提示用户输入两个数字
    printf("please input two number to calculate\n");
    int num1, num2;
    scanf("%d%d", &num1, &num2); // 读取用户输入的两个数字
    printf("%d\n", num1);        // 打印第一个数字
    printf("%d\n", num2);        // 打印第二个数字

    // 提示用户选择运算
    int choose;
    printf("please input one number to specify the operation you need\n");
    scanf("%d", &choose); // 读取用户输入的选择

    // 调用相应的运算函数并存储结果
    int res = (arr[choose - 1])(num1, num2); // 根据选择调用函数
    printf("%d\n", res);                     // 打印运算结果

    return 0; // 返回 0 表示程序正常结束
}