【APUE】文件I/O操作函数

本文详细介绍了Unix系统下C语言的文件操作函数,如open()用于打开或创建文件,read()、write()用于读写数据,close()关闭文件描述符等。还介绍了错误处理函数perror()、strerror(),以及文件位置、权限、删除、重命名等操作函数,帮助开发者进行文件操作和错误调试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

open()

open()是一个系统调用,用于在 Unix-like 操作系统中打开文件或创建新文件。它提供了对文件的访问和操作。
函数原型如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname,int flags);
int open(const char *pathname,int flags,mode_t mode);
  1. 参数 pathname是要打开或创建的文件的路径名。
  2. 参数 flags 是打开文件的标志,指定了文件的打开方式和操作模式。
  • O_RDONLY:只读方式打开文件
  • O_WRONLY: 只写方式打开文件
  • O_RDWR: 以读写方式打开文件
  • O_CREAT: 如果文件不存在,则创建文件
  • O TRUN: 如果文件存在并且为写入而打开,则将其截断为空文件
  • O_APPEND:在每次写入时将数据追加到文件未尾
  1. 可选参数 mode 仅在创建新文件时使用,指定文件的权限。它定义了所有者、所属组和其他用户对文件的读取、写入和执行权限。flag带O_CREAT选项时可以用来创建文件,这时必须带该参数用来指定创建文件的权限模式,如066。 否则不需要。(示例代码)
int fd; 
fd = open(“text.txt”, O_RDWR|O_CREAT|O_TRUNC, 0666); 
fd = open(“text.txt”, O_WRONLY|O_APPEND);

open()函数返回一个非负整文件描述符(file descriptor) ,并且该文件描述符是当前进程最小、未使用的文件描述符数值。用于后对文件进行读取、写入和其他操作。如果返回值为 -1,则表示打开文件发生错误,并设置全局变量 errno来指示具体的错误信息。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main() {
    int fd = open("example.txt", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
    if (fd == -1) {
        perror("无法打开文件");
        return 1;
    }
        
    // 对文件进行其他操作...
    
    close(fd);

    return 0;
}

在上述示例中,我们使用 opon()函数打开或创建一个名为"examdle.txt"的文件,并进行错误检查,我使用 O_WRONLY 标志以只写方式打开文件,并使用 O_CREAT标志来创建文件(如果文件不存在)。我们还用 S_IRUSRS_IWUSR来没置文的权限为读和写入权限。然后,我们可以对文件进行其他操作,最后使用 close()函数关闭文件描述符。

cerate()

int creat(const char *path, mode_t mode);

此函数用来创建一个新文件并返回其fd。它等价于 open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);

int fd; 
fd=creat(“text.txt”, 0644);

read()

read()是一个在 Unix/Linux 系统下用于从文件描述符中读取数据的系统调用函数。它的原型如下:

#include <unistd.h>

ssize_t read(int fd,void *buf,size_t count);

其中,参数的含义如下:

  • fd : 要读取的文件描述符(例如打开文件得到的文件描述符)。
  • buf : 接收读取数据的缓冲区指针,也就是将读取的数据存放到哪里。
  • count : 要读取的字节数,即期望读取的数据长度。

read()函数的返回值为实际读取的字节数,如果返回值为-1,则表示读取出错。具体错误可以通过检查 errno变量得到。
使用read()进行读取操作时,它会尽可地从文件中读取指定数量的字节数据,并将其存入指定的缓冲区中,返回实际读取的字节数。注意,它并不会处理字符串中的null 终止符,而是单纯地按照指定的字节数进行读取。

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

#define BUFFER_SIZE 1024

int main() {
    int fd;
    char buffer[BUFFER_SIZE];
    
    // 打开文件
    fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        perror("Failed to open file");
        exit(1);
    }
    
    // 读取文件内容
    ssize_t bytesRead = read(fd, buffer, BUFFER_SIZE);
    if (bytesRead == -1) {
        perror("Failed to read file");
        exit(1);
    }
    
    printf("Read %zd bytes from file: %.*s\n", bytesRead, (int)bytesRead, buffer);
    
    // 关闭文件
    close(fd);
    
    return 0;
}

在上述示例中,我们首先使用 open()函数打开一个名为"example.txt" 的文件,然后用read() 函数从该文件中读最多 BUFER_SIZE 字节的数据,并将其存在buffer 数组中。输出结果显示实际读取的字节数以及读取的内容。
需要注意的是,read()函数是一个阻塞函数,如果没有足够的数据可供读取,它将一直等待直到有足够的数据可读或发生错误,如果需要进行非阻塞读取,可以使用 fcntl()函数设置文件描述符的标志位。

write()

write()函数是一个系统调用,用于将数据从文件描述符 (file descriptor) 写入到文件或设备中
函数原型如下:

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
  • 参数fd是文件描述符,指定要写入数的目标文件或设备。
  • 参数 buf 是指向要写入数据的缓冲区的指针。
  • 参数 count 是要写入的字节数。

write()函数返回成功写入的字节数,如果出现错误则返回 -1,并设置全局变量 errno来指示具体的错误信息。
以下是一个简单的示例,演示如何使用 write()函数将数据写入文件:

#include <unistd.h>
#include <fcntl.h>

int main() {
    int fd = open("example.txt", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
    if (fd == -1) {
        perror("无法打开文件");
        return 1;
    }
    
    const char* message = "Hello, World!";
    ssize_t bytes_written = write(fd, message, strlen(message));
    if (bytes_written == -1) {
        perror("写入文件发生错误");
        close(fd);
        return 1;
    }
    
    close(fd);

    return 0;
}

在上述示中,我们首先用open()函数打开一个文件,并进行错误检查。然后,我们定义一个字串 message ,将其作为数据写入文件。我使用write()函数来执行实际的写入操作,并检查返回值以处理写入错误。最后,我们使用 close() 函数关闭文件描述符。

close()

int close(int fd);

该函数用来关闭一个打开的文件描述符,关闭一个文件时还会释放该进程加在该文件上的所有记录锁。当一个进程终止时,内核将会自动关闭它所有打开的文件。

perror()

perror()是一个C函数,用于将上一个函数调用发生的错误输出到标准流(stderr)。它可以帮助开发者追踪和调试程序中的错误。
perror()函数的声明如下:

#include <stdio.h>
void perror(const char *s);

perror()接受一个参数 s,它是一个指向以 null 结尾的字符串的指针。通常,s 参数用来提供关于错误的附加信息,以帮助识别具体的错误原因。
perror()函数会根据全局变量 errno的值,在标准错误流中打印相应的错误消息。 errno存储了最近一次函数调用失败时设置的错误码。
以下是一个示例,演示如何使用 perror() 函数打印与文件操作相关的错误消息:

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

int main() {
    FILE *file = fopen("nonexistent_file.txt", "r");
    if (file == NULL) {
        perror("Error");
        exit(EXIT_FAILURE);
    }
    
    // 文件操作...
    
    fclose(file);
    return 0;
}

//输出结果:Error: No such file or directory

在上述示中,fopen()函数尝试打开一个不存在的文件,并返回 NULL ,表示文打开失败。然后,我们用 perror()函数打印与此相关的错误消息当程序运行时,它会输出类似以下内容的错误消息:

Error: No such file or directory

通过使用 perror()函数,我们可以获得有关错误的更详细信息,以便进行适当的处理和调试。

  • 请注意,在调用 perror() 之前,必须包含<stdio.h> 和 <stdlib.h> 头文件,并且确保 errno变量已设置为正确的错误码。

strerror()

strerror()是个在C 语言中的函数,用于将错误码转换为对应的错误信息字符串。它的原型如下:

#include <string.h>
char *strerror(int errnum);

其中,errnum 参数是一个整数,表示错误码
strerror()函教会根据给定的错误码 errnum 返回对应的错误信息字符串,这些错误信息通定义在系统的错误码库中,每个错误码都有一个相应的错误描述。
以下是一个示例代码,演示了使用 strerror() 函数来获取错误信息字符串的过程:

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

int main() {
    int errnum = 2; // 示例错误码,表示文件或目录不存在
    const char *errMsg = strerror(errnum);
    
    printf("Error message for errno %d: %s\n", errnum, errMsg);
    
    return 0;
}

在上述示例中,我们将错码设为2,对应的错误是“No such fle or directry”(文件或录不存在)。通过调用 strerror()函数,我们可以获得这个错误码对应的错误信息字符串,并将其打印输出。

  • 需要注意的是,strerror()函数返回的错误信息字符串是一个指向静态存储区域的指针,因此不要试图修改或释放它。

lseek()

lseek() 是一个在 Unix/Linux 系统下用于改变文件位置偏移量的系统调用函数。它的原型如下:

include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

其中,参数的含义如下:

  • fd: 文件描述符,表示要进行操作的文件
  • offset : 位移量,表示要进行的位移大小
  • whence: 位移基准,指定 offset 是相对于哪个位置进行位移。它可以取以下值之一:
    • SEEK_SET : 将文件位置设置为距离文件开头 offset 个字节处。
    • SEEK_CUR :将文件位置设置为距离当前位置 offset 个字节处。
    • SEEK_END: 将文件位置设置为距离文件未尾 offset 个字节处。

lseek()函数可以用于调整文件的读/写位置。它允许你在文件中任意位置进行读/写操作,而不仅仅是顺序读/写。
以下是一个简单的示例,展示了如何使用 lseek() 函数改变文件位置:

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

int main() {
    int fd;
    off_t offset;
    
    // 打开文件
    fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        perror("Failed to open file");
        exit(1);
    }
    
    // 设置文件位置为距离开头 10 个字节处
    offset = lseek(fd, 10, SEEK_SET);
    if (offset == -1) {
        perror("Failed to seek file");
        exit(1);
    }
    
    printf("New file position: %lld\n", (long long)offset);
    
    // 关闭文件
    close(fd);
    
    return 0;
}

在上述例中,我们首先使用open()函数打开一个名为example.txt的文件然后用 lseek()将文件位置设置为距离文件开头 10 字节处。最后,我们输出设置后的文件位置。

  • 需要注意的是,lseek()函数返回设置后的文件位置,它是相对于文件开头的偏移量,如果成功,函数返回的值将是非负的;如果出错,将返回 -1,并设置相应的错误码。

access()

access()函数是一个用于检查文件或路径的访问权限的函数,在 Unix/Linux 系统中使用。它的原型如下:

#include <unistd.h>
int access(const char *path, int mode);

其中,参数的含义如下:

  • path : 要检查权限的文件或路径
  • mode : 要检查的访问模式
    access()函教通过将 path参教指定的文件或路径与mode参数中的访问模式进行比较,来判断是否有相应的权限。访问模式可以是以下值之一 (也可以通过按位或运算符 | 结合多个模式) :
  • F_OK : 检查文件是否存在
  • R_OK : 检查读取权限
  • W_OK : 检查写入权限
  • X_OK : 检查执行权限

返回值为 0表示具有相应的权限,返回值为 -1 表示没有相应的权限或发生了错误,可以通过 errno来获取具体的错误信息。
以下是一个简单的示例,展示如何使用 access() 函数检查文件的访问权限:

#include <stdio.h>
#include <unistd.h>

int main() {
    const char *path = "example.txt";
    
    // 检查文件是否存在
    if (access(path, F_OK) != 0) {
        perror("File does not exist");
        return 1;
    }
    
    // 检查读取权限
    if (access(path, R_OK) != 0) {
        perror("No read permission");
        return 1;
    }
    
    // 检查写入权限
    if (access(path, W_OK) != 0) {
        perror("No write permission");
        return 1;
    }
    
    // 检查执行权限
    if (access(path, X_OK) != 0) {
        perror("No execute permission");
        return 1;
    }
    
    printf("File has all necessary permissions\n");
    
    return 0;
}

在上述示例中,我们通过access()函数检查名为"example.txt"的文件是否存在以及是否具有读取、写入和执行权限。如果某个权限不可用,会打印相应的错误信息。

unlink()

unlink()函数是用于删除文件的函数,在Unix/Linux系统中使用。它的原型如下:

#include <unistd.h>
int unlink(const char *path);

其中,参数的含义如下:

  • path : 要删除的文件的路径

unlink ()函数会删除指定路径下的文件。如果成功删除文件,则返回值为0,如果发生错误,则返回值为 -1,并且可以通过 errno获取具体的错误信息。
以下是一个简单的示例,展示如何使用unlink ()函数删除文件:

#include <stdio.h>
#include <unistd.h>

int main() {
    const char *path = "example.txt";
    
    // 删除文件
    if (unlink(path) == -1) {
        perror("Failed to delete file");
        return 1;
    }
    
    printf("File deleted successfully\n");
    
    return 0;
}

在上述示例中,我们使用 unlink() 函数删除名“exampletxt"的文件,如果删除文件失败,将打的相应的错误信息; 如果成功删除文件,打印"File deleted successfully"
需要注意的是,一旦使用unlink()函数删除文件,文件将无法恢复。因此,在调用unlink()函数之前,请确保真正想要删除该文件。

dup()

dup()是一个在 Unix/Linux 系统下用于复制文件描述符的系统调用函数。它的原型如下:

#include <unistd.h>
int dup(int oldfd);

其中,参数的含义如下:

  • oldfd : 需要被复制的文件描述符。
    dup()函数用于复制文件描述符,并返回复制后的新文件描述符。新的文件描述符将指向与原给描述符相同的文件表项,这意味着两个文件描述符将共享文件偏移量和文件状态标志。
    以下是一个简单的示例,展示了如何使用 dup() 函数复制文件描述符:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    int fd, newfd;
    
    // 打开文件
    fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        perror("Failed to open file");
        exit(1);
    }
    
    // 复制文件描述符
    newfd = dup(fd);
    if (newfd == -1) {
        perror("Failed to duplicate file descriptor");
        exit(1);
    }
    
    printf("Original file descriptor: %d\n", fd);
    printf("Newly duplicated file descriptor: %d\n", newfd);
    
    // 关闭文件描述符
    close(fd);
    close(newfd);
    
    return 0;
}
//Original file descriptor: 3
//Newly duplicated file descriptor: 4

在上述示例中,我们首先使用 open() 函数打开一个名为example.txt的文件,并获 取现返回的文件描述符 fd,然后,我们使用dup()函数复制文件描述符 fd,并将复制后的新文件描述符 newfd 存储起来。最后,我们输出原始文件描述符和复制后的新文件描述符。
需要注意的是,du()函数返回一个新的文件描述符,如果成功,则为最小可用的未使用文件描述符,如果出错,则返回 -1,并设置相应的错误码。

dup2()

dup2()函数是在 Unix/Linux 系统下用于复制文件描述符的系统调用函数。它的原型如下:

#include <unistd.h>
int dup2(int oldfd, int newfd):

其中,参数的含义如下

  • oldfd : 需要被复制的文件描述符
  • newfd : 新的文件描述符,如果该文件描述符已经打开,则会先被关闭。

dup2()函数用于复制文件描述符,并将复制后的描述符与指定的新描述符绑定。如果新描述符 newfd 已经打开,则会先将其关闭,然后再进行复制,以下是一个简单的示例,展示了如何使用 dup2() 函数复制文件描述符;

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

int main() {
    int fd, newfd;
    
    // 打开文件
    fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        perror("Failed to open file");
        exit(1);
    }
    
    // 复制文件描述符到新的描述符
    newfd = dup2(fd, 10);
    if (newfd == -1) {
        perror("Failed to duplicate file descriptor");
        exit(1);
    }
    
    printf("Original file descriptor: %d\n", fd);
    printf("Duplicated file descriptor: %d\n", newfd);
    
    // 关闭文件描述符
    close(fd);
    close(newfd);
    
    return 0;
}
//Original file descriptor: 3
//Duplicated file descriptor: 10

在上述示例中,我们首先使用 open()函数打开一个名为“example.txt" 的文件,并获取返回的文件描述符 fd 。然后,我们使用 dup2()函数将文件描述符 fd 复制到新的描述符 10上,并将复制后的新文件描述符 newfd 存储起来。最后,我们输出原始文件描述符和复制后的新文件描述符。
需要注意的是,dup2()函数返一个新的文件描述符,如果复制成功,则为指定的新描述符,如果出错,则返回-1,并设置相应的错误码。

例子2
当使用 dup2()函数时,可以将一个文件描述符复制到另一个预先打开的、未被占用的文件描述符上以下是另一个示例,展示如何将标准输出重定向到一个文件中:

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

int main() {
    int fd, newfd;
    
    // 打开文件
    fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("Failed to open file");
        exit(1);
    }
    
    // 将标准输出重定向到文件
    newfd = dup2(fd, 1);
    if (newfd == -1) {
        perror("Failed to duplicate file descriptor");
        exit(1);
    }
    
    // 输出到标准输出,实际上将会写入文件
    printf("This message will be written to the file.\n");
    
    // 关闭文件描述符
    close(fd);
    
    return 0;
}

在上述示例中,我们首先用 open()函打开一个名为"output.txt" 的文件,并获返回的文件描述符fd。然后,我使用 dup2() 函将文件描符 fd 复制到标准输出的描述符 1 上,从而将标准输出重定向到文件。现在,任何输出到标准输出的内容都将被写入到文件“output.txt"中。
需要注意的是,我们在 open() 函数中用了 O_WRONLY | O_CREAT | O_TRUNC参数,这表示以只写方式打开文件,如文不存在则创建文件,如果文件已存在则将其截断为空文件。另外,0644 是指定新创建的文件的权限,其中0表示八进制。

stat()

stat()是一个用于获取文件或文件系统相关信息的函数,在 Unix/Linux 系统中广泛使用。它的原型如下:

#include <sys/types .h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *path, struct stat *buf);

其中,参数的含义如下:

  • path : 要获取信息的文件或文件夹路径
  • buf : 用于存储获取到的文件信息的结构体指针。

stat()函数会根据指定的路径 path 获取文件的元数据信息,并将结果存储在 struct_stat 结构体中。这个结构体定义如下。

struct stat {
    dev_t         st_dev;       /* 文件的设备 ID */
    ino_t         st_ino;       /* inode 号 */
    mode_t        st_mode;      /* 文件的类型和权限 */
    nlink_t       st_nlink;     /* 链接数 */
    uid_t         st_uid;       /* 文件所属用户的 ID */
    gid_t         st_gid;       /* 文件所属组的 ID */
    dev_t         st_rdev;      /* 设备号(如果是设备文件) */
    off_t         st_size;      /* 文件大小(字节数) */
    blksize_t     st_blksize;   /* I/O 块大小 */
    blkcnt_t      st_blocks;    /* 分配的块数量 */
    time_t        st_atime;     /* 最后访问时间 */
    time_t        st_mtime;     /* 最后修改时间 */
    time_t        st_ctime;     /* 文件状态更改时间 */
};

通过 stat()函数获取到的文件信息包括文件类型和访问权限、用户 ID 和组 ID、文件大小、最后访问、修改和状态更改时间等。
以下是一个简单的示例,展示如何使用stat() 函数获取文件的信息:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

int main() {
    const char *path = "example.txt";
    struct stat file_info;
    
    // 获取文件信息
    if (stat(path, &file_info) == -1) {
        perror("Failed to get file information");
        exit(1);
    }
    
    // 打印文件大小
    printf("File size: %lld bytes\n", (long long)file_info.st_size);
    
    // 打印最后修改时间
    printf("Last modified time: %s", ctime(&file_info.st_mtime));
    
    return 0;
}

在上述示例中,我们使用 stat()函数获取名为"example.txt 的文件的信息,并将结果存储在 file_info结构体中。然后,我们打了文件的大小和最后修改时间。

  • 请注意,file_info.st_size 是文件的大小(以字节为单位),file_info.st_mtime 是最后修改时间(表示为时间戳)。为了将时间转换为可读格式,我们使用了ctime()函教。

fstat()

fstat()函数是一个用于获取已打开文件相关信息的函数,在 Unix/Linux 系统中使用。它的原型如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int fstat(int fd, struct stat *buf);

其中,参数的含义如下

  • fd : 已打开文件的文件描述符。
  • buf : 用于存储获取到的文件信息的结构体指针。
    fstat()函数会根给定的文件描述符 fd 获取文件的元数据信息,并将结果存储在 struct stat 结构体中,它的结构体定义与 stat()函数中的相同。

以下是一个简单的示例,展示如何使用 fstat() 函数获取已打开文件的信息:

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

int main() {
    int fd;
    struct stat file_info;
    
    // 打开文件
    fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        perror("Failed to open file");
        exit(1);
    }
    
    // 获取文件信息
    if (fstat(fd, &file_info) == -1) {
        perror("Failed to get file information");
        exit(1);
    }
    
    // 打印文件大小
    printf("File size: %lld bytes\n", (long long)file_info.st_size);
    
    // 打印最后修改时间
    printf("Last modified time: %s", ctime(&file_info.st_mtime));
    
    // 关闭文件
    close(fd);
    
    return 0;
}

在上述示例中,我们首先使用 open()打开名为example.txt的文件,并获取文件描术符 fd。然后,我使用 fstat() 欧数获该文件的信息,并将结果存储在file_info 结构体中。最后,我们打印文件的大小和最后修改时间。

  • 请注意,与 stat()函数不同的是,fstat() 函数需要传入一个已打开文件的文件描述符而不是文件路径。

rename()

rename() 函数用于重命名文件或将文件移动到新位置。在C语言中,rename()函数的原型如下:

#include <stdio.h>
int rename(const char *oldpath, const char *newpath);

其中,参数的含义如下:

  • oldpath : 旧文件路径,即要重命名或移动的文件的当前路径newpath : 新文件路径,即要重命名或移动的文件的目标路径。
  • rename()函数将文件 oldpath 重命名为 newpath 或者将文件 oldpath 移动到 newpath,如果操功,函数返回0;如果发生错误,区数返回-1,并且可以使errno 获取具体的错误信息。

以下是一个示例,展示如何使用 rename() 函数重命名文件或将文件移动到新位置:

#include <stdio.h>

int main() {
    const char *oldpath = "oldname.txt";
    const char *newpath = "newname.txt";
    
    // 重命名文件
    if (rename(oldpath, newpath) == -1) {
        perror("Failed to rename file");
        return 1;
    }
    
    printf("File renamed successfully\n");
    
    return 0;
}

在上述示例中,我们使用rename()函数将名为"oldname.txt 的文件重命名为"newname.txt",如果重命名成功,将印"File renamed successfuly";如果失败,将打印相应的错误信息。

  • 需要注意的是,rename()函数也可以用于将文件移动到不同的目录,只需在 newpath 参数中给出新的路径即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

m0_51740466

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

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

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

打赏作者

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

抵扣说明:

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

余额充值