苏嵌实训——day13

零、概述

文件IO 进程 进程间通信,线程,线程同步,网络编程

一、文件IO

在这里插入图片描述

IO(input output)

1.1 学习IO的前提

  1. 为什么要学习IO
    嵌入式:由软件控制硬件,嵌入式需要对内存进行操作,和对内核进行操作,来完成操作硬件。
  2. IO的内容都有什么:主要学习已经提供好的API函数接口
  3. linux下一切皆文件
  4. linux是一个多用户多任务的操作系统 ----》进程
  5. 因为IO进程在应用层和内核层之交互,所以说学习时定义过于抽象
  6. linux操作系统把文件分为两大类
    • (1)二进制文件 —》a.out —>按照计算机的逻辑去存储
    • (2)文本文件 —》.c或者.txt —>按照程序员的逻辑去存储
  7. 底层存储是一样,逻辑存储上不一样。

1.2 IO是什么

IO指的是Input output,指的是对内存的输入和输出

在这里插入图片描述

1.3 如何使用IO

需要借助概念:linux下一切皆文件
所以说对文件的操作:打开文件---------读写文件----------关闭文件

1.4 IO的分类

  1. 文件IO:是由操作系统提供的API接口,也被称为系统调用,每个操作系统的IO—API接口是不一样的,也就造成了linux下的可执行我呢见如果调用了文件IO的话,是不能移植到别的平台的,因为文件IO的接口是由操作系统提供,不同操作系统接口是不一样的。
  2. 标准IO:stdio.h ----->是由标准C库 ANSI C库提供的API接口,标准IO是在文件IO的基础上封装出来的。

1.5 文件IO的接口

1. open
2. read
3. write
4. close
5. lseek
    注意:windows函数接口:_open _read _write _close
          ios函数接口:  Open Read Write Close

1.6 man手册的使用

man:是一个在线函数查询接口

1   Executable programs or shell commands( 可执行程序或者shell命令)
2   **System calls (functions provided by the kernel)(系统调用)(内核提供的函数)
3   **Library calls (functions within program libraries)(库调用)(程序库中的函数)
4   Special files (usually found in /dev) (特殊文件)(通常位于 /dev)  
5   File formats and conventions eg /etc/passwd (文件格式和规范)
6   Games (游戏)
7   Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7) 杂项(包括宏包和规范)
8   System administration commands (usually only for root) (系统管理命令)(通常只用于管理用户)
9   Kernel routines [Non standard](内核例程)【非标准】

1.7 errno

errno是一个全局变量,存在于#include <errno.h>中,用于标注错误信息,那么当函数报错返回时,会将这个全局变量重新根据错误信息表中的对用的号码设置一下
在这里插入图片描述

1.8 文件描述符

文件描述符是什么:一个文件的标志,是一个int类型的数据,非负数,值是小的。

在这里插入图片描述

注意:
./aout在刚运行时,内核会自动为他打开三个设备文件,返回三个文件描述:

  1. 标准输入 -----0
  2. 标准输出 -----1
  3. 标准错误输出 ----2

1.9 API接口的使用

附加:函数的三要素:功能,参数,返回值

1.9 1 open

1.头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


2.原型:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
3.功能:打开一个文件
4.参数:
pathname:路径及名称
        例如:"1.txt"    //以后参数是char*类型一般直接写字符串
flags:打开文件的方式
    必须包括:
        O_RDONLY:只读权限打开文件
        O_WRONLY:只写权限打开文件
        O_RDWR:读写方式打开文件
附加参数(以或的形式去附加)
        O_APPEND:以追加写的方式打开文件
        O_CREAT:如果文件存在则打开文件,如果文件不存在则创建文件,如果使用了O_CREAT这个参数,需要使用open的第三个参数,给与权限是一个8进制的整形,以0开头
        例如:0664
        O_EXCL:如果文件存在则报错返回
        O_NONBLOCK:以非阻塞方式打开一个文件
        O_TRUNC:以清空的方式打开文件
5.返回值:
    成功返回一个文件描述符
    失败返回-1;
注意:使用0666创建文件为什么出来的是0664呢?
    是由操作系统有一个叫做文件掩码的机制,将其它用户权限的可写权限抹掉
    使用umask查看文件掩码
    修改文件掩码 umask + 想要修改的权限值

在这里插入图片描述

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
int main(int argc, char const *argv[])
{
    int fd = open("./1.txt",O_RDWR|O_CREAT,0666);   //打开一个文件
    if(-1 == fd)
    {
        perror("open");  //打印错误信息
        printf("errno = %d\n",errno);
        return -1;
    }
    perror("open1");
    return 0;
}

1.9.2 perror

头文件:
    #include <stdio.h>


原型:
    void perror(const char *s);
功能:以固定格式打印错误信息
参数:s:判断的函数名字
返回值:无

1.9.3 read

头文件:
#include <unistd.h>
原型:
    ssize_t read(int fd, void *buf, size_t count);
功能:对一个文件进行读取
参数:fd:目标文件描述符  ----》对哪个文件进行读取
      buf:存放读到的数据 ----》读到的数据存放的地址
      count:读多少
返回值:成功:返回读到的字节个数
        失败:返回-1
        读到文件末尾返回0
注意:但是最后依次read时如果文件剩余字节不够read的count,读到文件末尾时返回已经读到的字节个数,再一次读取时返回0

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    //打开文件----》以读的方式打开
    int fd = open("./1.txt",O_RDONLY);
    if(-1 == fd)
    {
        perror("open");
        return -1;
    }
    //开始读取数据
    char buf[3] = {0};
    if(-1 == read(fd,buf,3))   //有多少空间读取多少个字节
    {
        perror("read");
        return -1;
    }
    printf("\n读取到的值为buf = %s\n",buf);
    //关闭文件描述符
    if(-1 == close(fd))
    {
        perror("close");
        return -1;
    }
    return 0;
}

1.9.4 write

头文件:
    #include <unistd.h>
原型:
    ssize_t write(int fd, const void *buf, size_t count);
功能:对一个文件进行写入操作
参数:fd:目标文件描述符 ----》对哪个文件进行操作
      buf:要写入的数据的地址----》要写入什么东西
      count:写入数据的大小  ---》写多少,以字节为单
返回值:
    成功:返回写入的字节个数
    失败: -1

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    int fd = open("./1.txt",O_WRONLY);
    if(-1 == fd)
    {
        perror("open");
        return -1;
    }
    //开始写入数据
    char buf[123] = "hello world";
    if(-1 == write(fd,buf,strlen(buf)))
    {
        perror("write");
        return -1;
    }
    return 0;
}

1.9.5 close

头文件:
    #include <unistd.h>

原型:
    int close(int fd);
功能:关闭一个文件
参数:fd要关闭的文件描述符
返回值:
    成功:返回 0
    失败: -1
注意:一个可执行程序最多打开1024个文件描述符,这个值可以通过内核去修改。

练习:先对一个文件进行写入helloworld,然后在从这个文件将helloworld读出来,打印到终端上。
结果:写入数据时正常,读取时未读到数据,这是因为读写指针的相互影响

1.9.6 lseek

头文件:
       #include <sys/types.h>
       #include <unistd.h>

原型:
     off_t lseek(int fd, off_t offset, int whence);
功能:操作读写指针,对读写指针进行偏移
参数:
    fd:目标文件描述符  ---》对哪个文件进行操作
    offset:如何偏移,偏移多少
        如果该数为负数,代表这向前进行偏移,如果偏移出了文件的开头,会报错返回
        如果该数为正数,代表这向后偏移,如果偏移除了文件的末尾,会扩大文件,用'\0'来填充,那么此类文件被称为---空洞文件---
    注意:如果偏移后没有对其进行任何写入操作内核认为该偏移无效,不会扩大文件大小。
    whence:基准位置  ----》根据哪一个位置进行偏移
       SEEK_SET :根据文件开头进行偏移
              The file offset is set to offset bytes.

       SEEK_CUR:根据用户当前位置进行偏移
              The file offset is set to its current location plus offset bytes.

       SEEK_END:根据文件末尾进行偏移
              The file offset is set to the size of the file plus offset bytes.
返回值:
    成功:返回偏移的字节个数(根据文件开头来定)
    失败: -1

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    int fd = open(argv[1],O_RDWR|O_CREAT,0666);
    if(-1 == fd)
    {
        perror("open");
        return -1;
    }
    //开始进行写入数据
    if(-1 == write(fd,"hello world",11))
    {
        perror("write");
        return -1;
    }
    if(-1 == lseek(fd,-11,SEEK_END))
    {
        perror("lseek");
        return -1;
    }
    char buf[123] = {0};
    if(-1 == read(fd,buf,sizeof(buf)))
    {
        perror("read");
        return -1;
    }
    printf("buf = %s\n",buf);
    close(fd);
    return 0;
}

1.10 练习:自己去实现cp命令 mycp(保证可以去拷贝二进制文件)

//读取时循环读取,写入时循环写入,使用argv参数
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#define SIZE 123
int main(int argc, char const *argv[])
{
    if(3 != argc)
    {
        printf("输入参数错误,例如:./a.out + 源文件 + 目标文件\n");
        return -1;
    }
    //打开要复制的文件以只读方式打开,打开要粘贴的文件,以写入并创建并且清空
    int fd = open(argv[1],O_RDONLY);
    if(-1 == fd)
    {
        perror("open");
        return -1;
    }
    int fd1 = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0664);
    if(-1 == fd1)
    {
        perror("open1");
        return -1;
    }
    //开始读取  ---》边读边写
    while(1)
    {
        //先读
        char buf[SIZE] = {0};//作为数据的中转站
        ssize_t ret = read(fd,buf,sizeof(buf));
        if(-1 == ret)
        {
            perror("read");
            return -1;
        }
        if(0 == ret)
        {
            printf("读取到文件的末尾,退出while循环\n");
            break;
        }
        //写入
        if(-1 == write(fd1,buf,ret))
        {
            perror("write");
            return -1;
        }
    }
    close(fd);
    close(fd1);
    return 0;
}

二、标准IO

2.1 标准IO的定义

标准IO是由标准C库提供的API接口,是在文件IO的基础上封装出来的函数接口,第一个原因是为了增加可移植性。并且,标准IO在文件IO的基础上封装了一个叫缓冲区的东西,用来存放一些不是很着急的数据,等到缓冲区满,调用一次文件IO完成写入工作或者读取工作。有些数据是特别着急的,

比如:下发的命令,实时监控数据,必须要使用文件IO。
伪代码:
    fopen(参数)if(is win)
        {
            _open(参数)}
        else if(is linux)
        {
            open(参数)}

2.1.1 文件流指针

FILE pointer
文件流指针:是一个文件的标志,是在文件描述符上封装出来的一个结构体指针,缓冲区就在这个流指针中。
流:流指针 -----文件流指针 —这个流
追代码:
安装一个索引搜索文件tags(搜索引擎)

第一步:创建目录的引擎文件—tags (想要搜索哪个目录就在哪个目录下创建)
ctags -R //生成一个引擎文件叫tags
第二步:搜索
vi -t 目标字符串 例如: vi -t FILE
第三步:选择准确的目录下的文件
第四步:翻阅文档----》代码跳转
追代码:光标点击想要跳转的字符串 ,ctrl + ]进行跳转搜索
返回上一层:ctrl + t

在这里插入图片描述

2.2 标准IO函数接口

2.2.1 fopen

头文件: #include <stdio.h>
原型:FILE *fopen(const char *pathname, const char *mode);
功能:打开一个文件
参数:pathname:要打开文件的路径及名称
mode:打开文件的方式
r:以只读的方式打开一个文件,文件必须存在
r+:以读写的方式打开一个文件,文件必须存在
w:如果文件存在则清空写入,如果文件不存在则创建写入
w+:如果文件存在则清空读写,如果文件不存在则创建读写
a:如果文件存在则追加写入,如果文件不存在则创建再写入
a+:如果文件不存在则创建文件并追加写入和读取,读取时从文件开头开始读取,写入时从文件的末尾开始写入,lseek偏移后不能进行写入操作
如果进行写入操作,则追加在末尾写入(不管之前如何偏移)
注意:有可能以后会遇到 rb+,b代表执行二进制操作,linux下无区分
返回值:
成功会返回一个文件流指针
失败返回NULL。

在这里插入图片描述

#include <stdio.h>
int main(int argc, char const *argv[])
{
    //打开一个文件需要定义一个文件流指针来指向fopen返回的已经封装好的文件流指针
    //FILE *fp = fopen("./1.txt","r");
    FILE *fp = fopen("./1.txt","w");
    if(NULL == fp)
    {
        perror("open");
        return -1;
    }
    printf("打开文件成功!\n");
    return 0;
}

2.2.2 fclose

头文件:
#include <stdio.h>
原型:
int fclose(FILE *stream);
功能:关闭一个文件流指针 //文件流指针里也绑定了一个文件的描述符
参数:目标文件流指针
返回值:
成功:返回 0
失败: -1

2.2.3. fgetc

头文件:
#include <stdio.h>
原型:
int fgetc(FILE *stream);
功能:从指定文件流指针中获取一个字符
参数:目标文件流指针
返回值:
成功:返回获取到的字符转化成int类型的数据
失败: 返回EOF
到达文件末尾: EOF

2.2.4 feof

头文件:
#include <stdio.h>
原型:
int feof(FILE *stream);
功能:判断是否到达了文件末尾
参数:目标文件流指针
返回值:如果到达了文件的末尾返回一个非0值
其它情况返回0

2.2.5 ferror

头文件:
#include <stdio.h>
原型:
int ferror(FILE *stream);
功能:判断文件操作是否出错
参数:目标文件流指针
返回值:如果文件操作失败了,返回一个非0值
其它情况返回0

#include <stdio.h>
int main(int argc, char const *argv[])
{
    FILE *fp = fopen("./1.txt","r");
    if(NULL == fp)
    {
        perror("fopen");
        return -1;
    }
    //开始做循环读取
    while(1)
    {
        int ret = fgetc(fp);
        if(EOF == ret)
        {
            if(feof(fp))
            {
                printf("到达了文件末尾!\n");
                break;
            }
            else
            {
                printf("获取失败!\n");
                return -1;
            }
        }
        printf("获取到的数据为%d---所对应的字符为%c\n",ret,ret);
    }
    if(fclose(fp) == EOF)
    {
        printf("关闭失败\n");
        return -1;
    }
    return 0;
}

2.2.6 fputc

头文件:
#include <stdio.h>
原型:
int fputc(int c, FILE *stream);
功能:向指定的一个文件输出一个字符
参数:c:想要写入的字符,将int转换成 unsigned char类型的数据写入
stream:目标文件流指针
返回值:
成功返回:写入的字符转换成int类型的数据
失败返回:EOF

#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    FILE *fp = fopen("./1.txt","w");
    if(NULL == fp)
    {
        perror("fopen");
        return -1;
    }
    //开始向文件中写入数据
    char buf[123] = "hello world";
    int i = 0;
    while(1)
    {
        fputc(buf[i],fp);
        if((strlen(buf) - 1) == i)
        {
            break;
        }
        i++;  //遍历数组使用
    }
    fclose(fp);
    return 0;
}

2.2.7 fgets

头文件:
#include <stdio.h>
原型:
char *fgets(char *s, int size, FILE *stream);
功能:行读取
参数:s:读取到的数据存放的地址
size:读取的字节个数
1.当\n之前的字节个数小于size,遇到\n结束读取
但是换行符也会被当做一个字符被读到内存之中,并且会在最后一个字符后添加’\0’.
2.当\n之前的字节个数大于size,会读到size个大小的字节时停止读取,
会在末尾加上一个’\0’,会将最后一个字节的字符给覆盖掉。
机制会在读取完之后读写指针向前偏移一个字节
stream:目标文件流指针
返回值:
成功返回:读到的字符串的首地址
失败返回:NULL
读到文件末尾:返回NULL

#include <stdio.h>
int main(int argc, char const *argv[])
{
    FILE *fp = fopen("./1.txt","r");
    if(NULL == fp)
    {
        perror("fopen");
        return -1;
    }
    //行读取
    while(1)
    {
        char buf[123] = {0};
        if(fgets(buf,11,fp) == NULL)
        {
            //失败或者读到文件末尾
            if(feof(fp))
            {
                printf("读到了文件的末尾\n");
                break;
            }
            else
            {
                perror("fgets");
                return -1;
            }
           
        }
        printf("buf = %s\n",buf);
    }
    fclose(fp);
    return 0;
}

2.2.8 fputs

头文件:
#include <stdio.h>
原型:
int fputs(const char *s, FILE *stream);
功能:向一个文件进行字符串输入
参数:s:想要写入的数据的地址
stream:目标文件流指针
返回值:
成功返回:一个非负数
失败返回:EOF(-1)

#include <stdio.h>

int main(int argc, char const *argv[])
{
    FILE *fp = fopen("./1.txt","w");
    if(NULL == fp)
    {
        perror("fopen");
        return -1;
    }
    //想要写入的数据的地址
    char buf[123] = "hello world";
    if(EOF == fputs(buf,fp))
    {
        perror("fputs");
        return -1;
    }
    fclose(fp);
    return 0;
}

练习:请编写一个代码,用来测试文件的行数

#include <stdio.h>
#include <string.h>


int main(int argc, char const *argv[])
{
    FILE *fp = fopen(argv[1],"r");
    if(NULL == fp)
    {
        perror("fopen");
        return -1;
    }
    int count = 0;
    //开始计算
    while(1)
    {
        char buf[123] = {0};
        if(NULL == fgets(buf,sizeof(buf),fp))
        {
            //出错或者到了文件末尾
            if(feof(fp))
            {
                printf("到达了文件的末尾\n");
                break;
            }
            else
            {
                perror("fgets");
                return -1;
            }
        }
        if(buf[strlen(buf) - 1] == '\n')
        {
            printf("buf = %s\n",buf);
            count++;
        }
        
    }
    count++;
    printf("该文件的行数为:%d\n",count);
    fclose(fp);
    return 0;
}

2.2.9 fread

头文件:
#include <stdio.h>
原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE stream);
功能:二进制读取文件
参数:ptr:存放读到的数据的地址
size:对象的大小
nmemb:对象的个数
注意:总的读取的字节个数为对象的大小
对象的个数
stream:目标文件流指针
返回值:
成功返回:成功返回读到的对象的个数
失败返回:0

2.2.10 fwrite

头文件:
#include <stdio.h>
原型:
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
功能:二进制写入文件
参数:ptr:想要写入的数据的首地址
size:对象的大小
nmemb:对象的个数
stream:目标文件流指针
返回值:
成功返回:成功返回读到的对象的大小
失败返回:返回一个小的对象的个数

#include <stdio.h>
#include <string.h>
struct person
{
    char name[32];
    char sex;
    int age;
    char phone[12];
};
struct person p1;  //定义一个录入使用的结构体
struct person p2;  //定义一个读取时使用的结构体

int main(int argc, char const *argv[])
{
STAT:
    //界面:
    printf("****************************\n");
    printf("1.录入信息 2 读取信息  3.退出\n");
    printf("****************************\n");
    printf("input>> ");
    int input = 0;
    scanf("%d",&input);
    if (input > 3 || input < 0)
    {
        goto STAT;
    }
    if(1 == input)
    {
        printf("请输入姓名\n");
        scanf("%s",p1.name);
        getchar();
        printf("请输入性别\n");
        scanf("%c",&p1.sex);
        getchar();
        printf("请输入年龄\n");
        scanf("%d",&p1.age);
        printf("请输入手机号\n");
        scanf("%s",p1.phone);
        getchar();
        FILE *fp = fopen("./1.txt","a");
        if(NULL == fp)
        {
            perror("fopen");
            return -1;
        }
        if(1 != fwrite(&p1,sizeof(p1),1,fp))
        {
            perror("fwrite");
            return -1;
        }
        fclose(fp);
        printf("写入信息结束,正在跳转菜单");
        goto STAT;
    }
    else if(2 == input)
    {
        char usrname[32] = {0};
        //读取信息
        printf("请输入要查询人的姓名\n");
        scanf("%s",usrname);
        getchar();
        //开始对文件进行读取并匹配
        FILE *fp = fopen("./1.txt","r");
        if(NULL == fp)
        {
            perror("fopen");
            return -1;
        }
        //循环读取
        while(1)
        {
            if(0 == fread(&p2,sizeof(p2),1,fp))
            {
                if(feof(fp))
                {
                    printf("读到文件末尾\n");
                    break;
                }
                else
                {
                    perror("fread");
                    return -1;
                }
            }
            if(strcmp(usrname,p2.name) == 0)
            {
                printf("姓名:%s\n",p2.name);
                printf("性别:%c\n",p2.sex);
                printf("年龄:%d\n",p2.age);
                printf("手机号:%s\n",p2.phone);
            }
        }


    }
    
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个简单的C语言万年历的实训报告,供您参考: 一、实训目的 本次实训的主要目的是通过编写一个简单的C语言万年历程序,巩固C语言基本语法和编程思想,提高编程能力和解决问题的能力。 二、实训要求 1、能够使用C语言基本语法和控制语句编写程序。 2、能够使用数组来存储和操作数据。 3、能够使用函数来实现模块化编程。 4、能够使用指针来操作内存中的数据。 5、能够使用文件来读取和写入数据。 三、实训内容 1、程序功能 本程序实现了一个简单的万年历功能,可以通过输入年份和月份来显示该月的日历。 2、程序实现 以下是本程序的主要代码实现: ```c #include <stdio.h> // 判断是否为闰年 int is_leap_year(int year) { if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { return 1; } else { return 0; } } // 获取某个月份的总天数 int get_days(int year, int month) { int days[] = {31, 28 + is_leap_year(year), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; return days[month - 1]; } // 获取某个日期是星期几 int get_weekday(int year, int month, int day) { if (month == 1 || month == 2) { year--; month += 12; } int c = year / 100; int y = year % 100; int w = y + y / 4 + c / 4 - 2 * c + 26 * (month + 1) / 10 + day - 1; w = (w % 7 + 7) % 7; return w; } // 显示日历 void show_calendar(int year, int month) { int days = get_days(year, month); int weekday = get_weekday(year, month, 1); printf(" 日 一 二 三 四 五 六\n"); int i; for (i = 0; i < weekday; i++) { printf(" "); } for (i = 1; i <= days; i++) { printf("%2d ", i); if ((weekday + i) % 7 == 0) { printf("\n"); } } if ((weekday + days) % 7 != 0) { printf("\n"); } } int main() { int year, month; printf("请输入年份:"); scanf("%d", &year); printf("请输入月份:"); scanf("%d", &month); if (month < 1 || month > 12) { printf("月份输入错误!\n"); return 1; } printf(" %d年%d月\n", year, month); show_calendar(year, month); return 0; } ``` 四、实训总结 通过本次实训,我学会了如何使用C语言来编写一个简单的万年历程序,巩固了C语言基本语法和编程思想,加强了对函数、数组、指针、文件等概念和用法的理解,提高了编程能力和解决问题的能力。同时,我也意识到在编程过程中需要注重代码的规范、可读性和可维护性,这对于日后的开发工作非常重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周末不下雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值