C语言 指针(特别篇)

这篇具有很好参考价值的文章主要介绍了C语言 指针(特别篇)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

C语言 指针(特别篇)

C语言是一种十分重要的编程语言,广泛应用于计算机领域,尤其是操作系统、编译器、网络通信等方面。其中,指针是C语言中非常重要的概念和基础,本文主要介绍C语言中各种指针的用法。


内存地址

学好C语言指针的关键在于要深刻理解计算机中的内存地址

计算机中的内存地址是指用来唯一标识存储单元的值。这些存储单元按照连续的方式构成了计算机的内存空间,每个存储单元可以存储一个字节(8位)的数据。

内存地址通常以十六进制表示,它们从0开始递增,直到最大地址。在32位系统中,最大地址为0xFFFFFFFF(4GB),而在64位系统中,最大地址可达到0xFFFFFFFFFFFFFFFF(18EB,1EB等于10^9GB)。

内存地址空间可分为以下几个部分:

  1. 代码段(Code Segment):
    代码段存储程序的机器指令,也称为可执行代码。它是只读的,用来存放程序的指令集和常量数据。

  2. 数据段(Data Segment):
    数据段存储程序的全局变量、静态变量和静态常量。它包含了已经初始化或默认初始化的数据,并且在程序运行期间不会发生变化。

  3. BSS段(Block Started by Symbol):
    BSS段存储未初始化的全局变量和静态变量。在程序加载时,BSS段的变量会自动被初始化为0或空指针。

  4. 堆(Heap):
    堆是动态分配内存的区域,用于存储程序运行时动态申请的数据。在C语言中,通过函数如malloc()和free()来管理堆内存的分配与释放。

  5. 栈(Stack):
    栈用于存储程序运行时的局部变量、函数参数和调用信息。栈是一种先进后出的数据结构,通过指针栈顶位置来实现栈帧的压入和弹出。

  6. 运行时堆栈:
    运行时堆栈是保存函数调用过程中使用的局部变量、中间结果和返回地址的区域。每个函数调用都会在运行时创建一个新的堆栈帧,并在函数返回时销毁。

这些内存地址空间的划分使得程序能够有效地管理内存资源,并提供了不同类型数据的存储区域。对于程序员来说,理解计算机的内存地址模型是编写高效、可靠代码的基础之一。


简要介绍C语言指针

C语言的指针是一种变量,它可以存储内存地址作为值。指针的 本质是通过存储内存地址来提供对数据的间接访问和操作能力

在计算机中,内存被划分成一个个存储单元,每个存储单元都有唯一的地址。在C语言中,指针允许我们将这些地址作为值存储起来,并通过指针来访问和修改所指向的内存单元中存储的数据。

指针的本质是在内存中存储的一个整数值,该值代表某个特定内存单元的地址。通过使用指针,我们可以直接操作内存中的数据,无需通过变量名来访问。这为程序员提供了更灵活、高效地处理数据的能力。

指针的特点有以下几点:

  1. 指针保存变量的内存地址,而不是变量的实际值。
  2. 通过指针可以直接读取或修改指向的内存单元中的数据。
  3. 指针在使用前需要进行初始化,即将指针指向特定地址。
  4. 可以通过指针进行数据的传递和共享,使得函数可以修改传入的变量的值。

总结起来,C语言的指针提供了一种强大的工具能够在程序中灵活地操作内存中的数据。通过指针,我们可以实现动态内存管理、数组和字符串处理、数据结构的构建等功能。理解指针的本质能够帮助程序员更好地利用C语言进行开发和优化。C语言指针对于一些初学者来说可能很难,但是一旦掌握了C语言指针,将会大大提高编程的效率。


C语言的指针可以指向什么?

  1. 变量:指针可以指向不同类型的变量,包括整型、浮点型、字符型等。通过指针,可以访问和修改指向变量所存储的值。
  2. 数组元素:数组名本质上是指向数组首元素的指针,可以使用指针来遍历数组,访问和修改数组元素的值。
  3. 字符串:字符串实际上是由一系列字符组成的字符数组,在C语言中以空字符(‘\0’)结尾。可以使用指针来操作字符串,包括遍历、拷贝和连接等操作。
  4. 结构体:结构体是用户自定义的复合数据类型,可以包含多个不同类型的成员。指针可以指向结构体变量,允许通过指针访问和修改结构体中的成员。
  5. 动态分配的内存:C语言提供了动态内存管理的功能,可以使用指针来指向通过malloc()、calloc()等函数动态分配的内存块,并在不需要时释放该内存。
  6. 函数:在C语言中,函数也被视为一种特殊的数据类型。指针可以指向函数,称为函数指针。通过函数指针,可以调用相应的函数及传递函数作为参数。

取地址符 &(Address-of Operator)

在C语言中,取地址符(Address-of Operator)用于获取变量的地址。取地址符使用符号"&"表示,放置在变量名之前。

以下是取地址符的使用示例:

int num = 10;
int *ptr = # // 取得变量num的地址,并将其赋值给指针ptr

printf("变量num的地址:%p\n", &num);
printf("指针ptr存储的地址:%p\n", ptr);

C语言中的 * 号运算符

在C语言中,星号(*)是一元运算符,具有多种作用,取决于它所用的上下文。以下是星号运算符在C语言中的几种常见作用:

  1. 声明指针类型:在变量声明时,星号可以用作指针类型的标识符。例如,int *ptr; 声明了一个名为ptr 的指向整型变量的指针。
  2. 解引用操作符(Dereference Operator):使用星号对指针进行解引用操作,可以访问指针所指向的内存地址存储的值。例如,int x = *ptr; 将会将指针 ptr 指向的内存地址的值赋给变量 x
  3. 定义函数指针:使用星号可以定义函数指针,用于存储函数的地址。例如,int (*funcPtr)(int, int); 定义了一个指向接受两个int类型参数并返回 int 类型值的函数的指针。
  4. 乘法运算符:星号还可以用于乘法运算,表示两个数相乘的结果,此时 * 号就变成了二元运算符。例如,int result = a * b; 将变量 ab 相乘的结果赋给变量 result

示例集:

指向变量的指针

int num = 10;
int *p;       // 定义一个整型指针
p = #     // 将指针p指向变量num的内存地址, & 为取地址符, 可以将变量的内存地址取出
printf("%d\n", *p);    // 输出变量num的值

C语言 指针(特别篇)

指向数组的指针

C语言中,数组名本身就是指向数组首个元素的指针,可以通过指针访问整个数组的元素。

int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;     // 数组名arr即是指向数组首个元素的指针
for (int i = 0; i < 5; i++) {
    printf("%d ", *(ptr + i));   // 输出数组元素
}

C语言 指针(特别篇)

数组名本身就是一个指针:

int arr[5] = {1, 2, 3, 4, 5};
printf("数组的首地址为 %d\n", arr);
for (int i = 0; i < 5; i++) {
    printf("内存地址为 %d 中存储的数据为 %d\n", arr, *(arr + i));   // 输出数组元素
}

C语言 指针(特别篇)

指向字符串的指针

#include <stdio.h>

int main() {
    char *name = "John"; // 指向字符常量"John"的指针
    printf("Name: %s\n", name); // 使用%s格式化输出字符串
    
    return 0;
}

C语言 指针(特别篇)

#include <stdio.h>

int main() {
    char *fruits[] = {"Apple", "Banana", "Orange"}; // 字符串指针数组
    int i;

    for (i = 0; i < 3; i++) {
        printf("Fruit: %s\n", fruits[i]); // 使用循环遍历并输出每个字符串
    }

    return 0;
}

C语言 指针(特别篇)

字符串的本质就是字符数组,也可以用指向数组的指针来操作字符串。

二级指针

二级指针 就是 指向 指针变量 的 指针。指针变量本质也是变量,只不过这个变量存储的是其他变量的内存地址罢了。既然是变量就有内存地址,所以还可以再定义一个指针变量,用再定义的指针变量来存储前面那个指针变量的内存地址,于是后定义的指针变量就指向了前面的指针变量。这个后定义的指针变量就是二级指针。

int num = 10;
int *p1 = &num;    // 指向变量num的指针
int **p2 = &p1;    // 指向指针p1的指针(二级指针)
printf("%d\n", **p2);    // 输出变量num的值

C语言 指针(特别篇)

指针数组的数组名是一个二级指针
#include <stdio.h>
#include <string.h>
int main()
{
	int i;
	char *color[5]={"red","blue","yellow","green","black"};    //字符串数组, 或者说指针数组
	char **pc;    //二级指针
	char str[20];
	pc=color;
	printf("Input a color:");
	scanf("%s",str);
	for(i=0;i<5;i++)
		if(strcmp(str,*(pc+i))==0)
		 	break;
	if(i<5)
		printf("position:%d\n",i+1);
	else
		printf("Not Found\n");
	return 0;
}

二维数组 || 矩阵 || 二级指针
#include <stdio.h>
#include <stdlib.h>

// 函数:创建并初始化矩阵
int** createMatrix(int rows, int cols) {
    int** matrix = (int**)malloc(rows * sizeof(int*)); // 分配行指针数组的内存空间

    for (int i = 0; i < rows; i++) {
        matrix[i] = (int*)malloc(cols * sizeof(int)); // 分配每一行的内存空间

        for (int j = 0; j < cols; j++) {
            matrix[i][j] = i + j; // 初始化矩阵元素的值
        }
    }

    return matrix;
}

// 函数:释放矩阵的内存空间
void freeMatrix(int** matrix, int rows) {
    for (int i = 0; i < rows; i++) {
        free(matrix[i]); // 释放每一行的内存空间
    }

    free(matrix); // 释放行指针数组的内存空间
}

// 函数:打印矩阵
void printMatrix(int** matrix, int rows, int cols) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", matrix[i][j]); // 输出矩阵元素的值
        }
        printf("\n");
    }
}

int main() {
    int rows = 3, cols = 3;
    int** matrix = createMatrix(rows, cols);

    printf("Matrix:\n");
    printMatrix(matrix, rows, cols);

    freeMatrix(matrix, rows);

    return 0;
}

C语言 指针(特别篇)

指向结构体的指针

typedef struct {
    char name[20];
    int age;
} Person;

Person person;
Person *ptr = &person;    // 指向结构体Person的指针
strcpy(ptr->name, "Tom"); // 修改结构体成员name的值
ptr->age = 25;            // 修改结构体成员age的值
printf("%s %d\n", ptr->name, ptr->age);   // 输出结构体成员的值

对于指向结构体的指针可以参考我的往期文章,内容很详细 : C语言结构体数组+结构体类型指针+指向结构体数组的指针+typedef类型

动态申请内存

动态申请内存的两个函数 malloc()calloc() 函数声明包含在 stdlib.h 头文件中。

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

int main() {
    int size;
    int *arr;

    printf("请输入数组大小:");
    scanf("%d", &size);

    // 动态分配内存
    arr = (int *)malloc(size * sizeof(int));

    if (arr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }

    // 初始化数组元素
    for (int i = 0; i < size; i++) {
        arr[i] = i + 1;
    }

    // 打印数组元素
    printf("数组元素:");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }

    // 释放内存
    free(arr);

    return 0;
}

上述代码中,首先通过scanf函数获取用户输入的数组大小。然后使用malloc函数动态分配大小为size乘以sizeof(int)的内存空间,并将返回的指针赋值给整型指针变量arr。

接着,我们通过遍历数组对其元素进行初始化。最后,使用循环打印数组元素。

在程序末尾,使用free函数释放动态分配的内存空间,防止内存泄漏。

请注意,在使用完动态分配的内存后,一定要记得及时释放,以确保不会造成内存泄漏。

C语言 指针(特别篇)

以下是一个使用 calloc 函数的例子:

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

int main() {
    int size;
    int *arr;

    printf("请输入数组大小:");
    scanf("%d", &size);

    // 使用calloc动态分配内存,并初始化为零
    arr = (int *)calloc(size, sizeof(int));

    if (arr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }

    // 打印数组元素
    printf("数组元素:");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }

    // 释放内存
    free(arr);

    return 0;
}

在上述代码中,我们先通过scanf函数获取用户输入的数组大小。然后使用calloc函数动态分配大小为size乘以sizeof(int)的内存空间,并将返回的指针赋值给整型指针变量arr。

由于使用了calloc函数,所分配的内存会被自动初始化为零。因此,在打印数组元素时,我们可以看到初始时它们都是零。

同样地,在程序末尾,使用free函数释放动态分配的内存空间,防止内存泄漏。

C语言 指针(特别篇)

指向函数的指针

C语言中,函数其实也有一个入口地址。我们可以用指针来指向函数。函数入口地址是指函数在内存中的起始位置的地址。每个函数都有一个唯一的函数入口地址,它表示函数在可执行程序中的位置,让程序能够定位并调用该函数。

函数入口地址通常由编译器在编译阶段确定,并在链接器将各个模块合并成可执行程序时进行填充。当程序调用一个函数时,实际上是通过函数入口地址来跳转到该函数的代码执行处。

在C语言中,函数入口地址可以使用函数指针来表示和操作。函数指针是一个特殊类型的指针,它可以存储函数的入口地址,以便后续调用该函数。通过获取函数的入口地址,我们可以将其赋值给函数指针,并通过该指针间接调用函数。

#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); // 声明一个指向函数的指针

    operation = add; // 将add函数的地址赋值给指针
    printf("Result of addition: %d\n", operation(5, 3)); // 通过指针调用add函数

    operation = subtract; // 将subtract函数的地址赋值给指针
    printf("Result of subtraction: %d\n", operation(5, 3)); // 通过指针调用subtract函数

    operation = multiply; // 将multiply函数的地址赋值给指针
    printf("Result of multiplication: %d\n", operation(5, 3)); // 通过指针调用multiply函数

    return 0;
}

C语言 指针(特别篇)

指向函数的指针压入栈中实现递归调用

当我们运行递归函数的时候,操作系统其实做的就是将每次调用的函数地址压入栈中。

在函数递归调用时,函数指针的压入栈中可以实现递归调用的原理。具体来说,当一个函数通过函数指针调用自身时,它需要将函数指针的值压入栈中,并保留其他必要的参数和局部变量。

下面是一个简单的示例代码,展示了利用函数指针实现递归调用的原理:

#include <stdio.h>

// 定义递归函数
int recursiveFunc(int n, int (*func)(int)) {
    if (n <= 0) {
        return 0;
    }
    
    // 调用函数指针指向的函数,并将结果与递归调用相加
    return func(n) + recursiveFunc(n - 1, func);
}

// 定义一个打印数字的函数
int printNumber(int num) {
    printf("%d ", num);
    return num;
}

int main() {
    int n = 5;
    
    // 将打印数字的函数指针作为参数传递,并压入栈实现递归调用
    int result = recursiveFunc(n, printNumber);
    
    printf("\n结果:%d\n", result);
    
    return 0;
}

在上面的例子中,我们定义了一个递归函数recursiveFunc,它接受两个参数:n表示递归的终止条件,func表示指向函数的指针。递归函数首先判断终止条件,如果满足则返回0;否则,调用函数指针指向的函数,并将结果与递归调用的结果相加。

我们还定义了一个打印数字的函数printNumber,它接收一个整数并在控制台上打印该数字。在main函数中,我们将打印数字的函数指针作为参数传递给递归函数,并将起始值设为5。

当程序运行时,递归函数会依次调用打印数字的函数,并在每次调用时打印当前的数字。最后,递归函数的结果被打印出来。

通过将指向函数的指针作为参数传递并压入栈,可以实现函数的递归调用。这种方法可以动态地指定需要执行的函数,并在函数执行过程中保持递归的状态。

C语言 指针(特别篇)

多级指针

理论上指针的级数可以无限增长,但是通常没用应用的必要。记住一点,只有同级指针且指针指向的数据类型相同时,可以相互赋值

三级指针
#include <stdio.h>
#include <stdlib.h>

void printMatrix(int **matrix, int rows, int cols) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }
}

void createMatrix(int ***matrix, int rows, int cols) {
    *matrix = (int **)malloc(rows * sizeof(int *));
    for (int i = 0; i < rows; i++) {
        (*matrix)[i] = (int *)malloc(cols * sizeof(int));
        for (int j = 0; j < cols; j++) {
            (*matrix)[i][j] = i * cols + j;
        }
    }
}

void freeMatrix(int ***matrix, int rows) {
    for (int i = 0; i < rows; i++) {
        free((*matrix)[i]);
    }
    free(*matrix);
    *matrix = NULL;
}

int main() {
    int **matrix = NULL;
    int rows = 3;
    int cols = 3;

    createMatrix(&matrix, rows, cols);

    printf("Matrix:\n");
    printMatrix(matrix, rows, cols);

    freeMatrix(&matrix, rows);

    return 0;
}

在这个例子中,我们使用三级指针 int ***matrix 来操控矩阵。首先,在 createMatrix 函数中,我们通过传递指向指针的指针来分配内存并创建矩阵。然后,在 printMatrix 函数中,我们使用二级指针 int **matrix 来遍历和打印矩阵的元素。最后,在 freeMatrix 函数中,我们使用三级指针来释放矩阵占用的内存。

请注意,在操作三级指针时需要小心管理内存,并确保正确地分配和释放内存,以避免内存泄漏和错误。

三级指针的应用
三维数组在编程中有许多应用场景,特别是在涉及到多维数据的存储和处理时非常有用。以下是一些常见的三维数组的应用示例:

  1. 三维图像处理:在计算机图形学和图像处理领域,三维数组经常用于表示和处理彩色图像或体积数据。图像可以被看作是由像素组成的二维阵列,而每个像素又包含红、绿、蓝(RGB)或其他颜色通道的值,这样就可以使用三维数组来表示图像数据。

  2. 三维空间建模:在三维建模、游戏开发和虚拟现实中,三维数组可以用于表示三维空间中的物体、场景或地形。例如,一个三维场景可以被分成一个网格,每个网格单元包含物体的属性、纹理信息或碰撞检测数据等。

  3. 多维物理模拟:在物理模拟和科学计算中,三维数组可以用于存储和更新三维空间中的物理量,如速度场、压力场或温度场。通过使用三维数组,可以对不同位置上的物理量进行存储和操作,并模拟复杂的物理过程。

  4. 数据立方体:三维数组还可以用于表示和分析包含多个维度的数据集,例如销售数据、气象数据或市场调查数据。这些数据通常以数据立方体(data cube)的形式进行分析和查询,其中三维数组的每个维度对应于数据集中的一个属性。

这些只是三维数组的一些常见应用示例,实际上,它们在各种领域和问题中都具有广泛的应用。使用三维数组时,需要理解索引和访问元素的方式,并根据具体的问题进行适当的操作和算法设计。文章来源地址https://www.toymoban.com/news/detail-486001.html

到了这里,关于C语言 指针(特别篇)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 十分钟配置好Neovim go开发环境(其他语言一样)

    这篇文章的目的是为了分享下我自己的 Neovim 配置。 本人是Golang程序员,最开始使用的IDE是 JetBrains Goland 。有一说一这个ide适配度很高,认识的很多人都使用这个。但是它也有几个对我来说的缺点: 内存占用度高,我个人电脑配置不太跟的上 费用较高(DDDD) 去年的时候换了

    2024年02月15日
    浏览(51)
  • 《每天十分钟》-红宝书第4版-语言基础-数据类型(一)

    关于ECMAScript 数据类型,“非常6+1” 6:六种简单数据类型(也称为原始类型) Undefined Null Boolean Number String Symbol(ES6新增) 1:一种复杂数据类型 Object 使用 typeof 操作符 (注意是操作符)可以判断一个变量的数据类型 \\\"undefined\\\"表示值未定义; \\\"boolean\\\"表示值为布尔值; \\\"string\\\"表示

    2024年02月13日
    浏览(41)
  • 《每天十分钟》-红宝书第4版-语言基础-数据类型(五)

    这个符号作为一个属性表示“一个布尔值,如果是 true,则意味着对象应 该用 Array.prototype.concat()打平其数组元素”。ES6 中的 Array.prototype.concat()方法会 根 据 接 收 到 的 对 象 类 型 选 择 如 何 将 一 个 类 数 组 对 象 拼 接 成 数 组 实 例 。 覆 盖 Symbol.isConcat- Spreadable 的值可

    2024年02月14日
    浏览(42)
  • 【初阶C语言3】特别详细地介绍函数以及在初阶中重要的算法——递归

     💓作者简介: 加油,旭杏,目前大二,正在学习 C++ , 数据结构 等👀 💓作者主页:加油,旭杏的主页👀 ⏩本文收录在:再识C进阶的专栏👀 🚚代码仓库:旭日东升 1👀 🌹欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖        从标题也能看出来,我们有要进行 超详细

    2024年02月08日
    浏览(47)
  • 微软近日推出了Phi-2,这是一款小型语言模型,但其性能却十分强大

    每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领域的领跑者。点击订阅,与未

    2024年02月04日
    浏览(73)
  • 一种编程语言,

     前言:相信看到这篇文章的小伙伴都或多或少有一些编程基础,懂得一些linux的基本命令了吧,本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python:一种编程语言,只能进行后端数据的处理和管理前端

    2024年02月10日
    浏览(41)
  • 【C语言】指针进阶:字符指针&&数组指针&&函数指针

    ✨作者:@平凡的人1 ✨专栏:《C语言从0到1》 ✨一句话:凡是过往,皆为序章 ✨说明: 过去无可挽回, 未来可以改变 🌹 感谢您的点赞与关注,同时欢迎各位有空来访我的 🍁平凡舍 回想之前,我们学了 指针 的一些基础👉 指针与结构体 我们知道了指针的概念: 指针就是

    2023年04月08日
    浏览(43)
  • 一种峰值检测算法——AMPD算法(C语言实现)

    本文算法的原始论文出处:Algorithms | Free Full-Text | An Efficient Algorithm for Automatic Peak Detection in Noisy Periodic and Quasi-Periodic Signals | HTML (mdpi.com) 有位老哥在知乎写了Python代码:python代码 在数字信号处理中,经常涉及到波峰查找算法,如振动信号分析,样条插值法求包络等。对于周

    2024年02月03日
    浏览(38)
  • c语言指针(深入了解指针)

    前沿:       有人曾说过不会指针等于没有学习c语言,而我也是非常认同这个观点的,要想学习好c语言,指针是比不可缺少的,如果指针学不会c语言也就没办法学好,而向如此越重要的东西越比较难学,但难学并不代表学不会,这片文章将由简单到复杂让你深刻的了解指针

    2023年04月08日
    浏览(47)
  • 【C语言】——指针四:字符指针与函数指针变量

      在前面的学习中,我们知道有一种指针类型为 字符指针: c h a r ∗ char* c ha r ∗ 。下面我们来介绍它的使用方法。    使用方法:      如果我们想 存储字符串 ,可以用什么方法呢?之前我们一般都是用 字符数组 ,那还有什么办法呢?其实, 字符指针 也是可以

    2024年04月12日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包