嵌入式软件开发 day15(文件和目录)

文件和目录

一、stat()

描述:把文件pathname的属性填入到结构体buf

手册man 2 stat

注意:stat与lstat区别

stat:

展开符号链接,回填的是符号链接所指向的文件的属性

fstat:

回填的是所打开文件的文件属性

lstat:

不展开符号链接,回填的是符号链接本身的文件属性

头文件

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

函数原型

int stat(const char *pathname, struct stat *buf);
  • pathname – 想要获取的文件

  • statbuf – 一个要回填的struct stat结构体类型的指针

成员含义
dev_t st_dev存的是文件本身存储设备的设备号,也就是硬盘的设备号
ino_t st_inoinode索引号
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当前文件系统中每一数据块的大小
blkcnt_t st_blocks当前文件占了多少512字节的块
struct timespec st_atim文件上一次打开的时间
struct timespec st_mtim文件内容上一次变动的时间
struct timespec st_ctiminode 上一次变动的时间

查看结构体:man 2 stat

struct stat 
 {
               dev_t     st_dev;         /* ID of device containing file */
               ino_t     st_ino;         /* Inode number */
               mode_t    st_mode;        /* File type and mode */
               nlink_t   st_nlink;       /* Number of hard links */
               uid_t     st_uid;         /* User ID of owner */
               gid_t     st_gid;         /* Group ID of owner */
               dev_t     st_rdev;        /* Device ID (if special file) */
               off_t     st_size;        /* Total size, in bytes */
               blksize_t st_blksize;     /* Block size for filesystem I/O */
               blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */

               /* Since Linux 2.6, the kernel supports nanosecond
                  precision for the following timestamp fields.
                  For the details before Linux 2.6, see NOTES. */

               struct timespec st_atim;  /* Time of last access */
               struct timespec st_mtim;  /* Time of last modification */
               struct timespec st_ctim;  /* Time of last status change */

           #define st_atime st_atim.tv_sec      /* Backward compatibility */
           #define st_mtime st_mtim.tv_sec
           #define st_ctime st_ctim.tv_sec
 };

注意:

off_t     st_size;        /* Total size, in bytes */
blksize_t st_blksize;     /* Block size for filesystem I/O */
blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */

三个结构体成员没有任何关系

size只是一个属性而已

说明文件大小与文件磁盘上所占空间没有任何关系

返回值

成功:0

失败:-1,设置errno

代码演示

功能:获取文件大小

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


off_t flen(const char *fname)
{
	struct stat statres;
	if(stat(fname,&statres) < 0)
	{
		perror("stat()");
		exit(1);
	}
	return statres.st_size;
}
int main(int argc,char *argv[])
{

	if(argc < 2)
	{
		fprintf(stderr,"Usage:%s file\n",argv[0]);
		exit(1);
	}
	
	printf("%lld\n",(long long)flen(argv[1]));
	exit(0);
}

功能:证明文件大小与文件磁盘上所占空间的关系

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


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

	if(argc < 2)
	{
		fprintf(stderr,"Usage:%s file\n",argv[0]);
		exit(1);
	}
	
	fd = open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,0600);
	if(fd < 0)
	{
		perror("open()");
		exit(1);
	}
	/*生成5G大小的空洞文件,要加单位LL*/
	lseek(fd,1024LL*1024LL*1024LL*5LL-1LL,SEEK_SET);
	write(fd,"",1);

	close(fd);
	exit(0);
}

功能:获取文件类型

手册man inode

在这里插入图片描述

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


int  ftype(const char *fname)
{
	struct stat statres;

	if(stat(fname,&statres) < 0)
	{
		perror("stat()");
		exit(1);
	}

	if(S_ISREG(statres.st_mode))
		return '-';
	else if(S_ISDIR(statres.st_mode))
			return 'd';
		else 
			return '?';
}

int main(int argc,char *argv[])
{
	if(argc < 2)
	{
		fprintf(stderr,"Usage:%s file\n",argv[0]);
		exit(1);
	}
	
	printf("%c\n",ftype(argv[1]));
	exit(0);
}

拓展

工具:ctags

默认安装 > /usr/include

作用:查看定义类型的原型

用法:vim -t ssize_t

在包含头文件前需要定义宏时两种方式

方式一

命令行:gcc -D_ FILE_OFFSET_BITS =64 flen.c

-D:在命令行定义宏

方式二

编译选项:CFLAGS += -D_ FILE_OFFSET_BITS =64

在makefile中

命令du:打印文件在磁盘中真正所占空间大小

例题:写一个程序,功能:实现du的功能

代码演示

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


#define PATHSIZE	1024

int path_no_loop(const char *path)
{// ../io/..a   /etc/.   mydir/..  /etc/a/b/c/d/..a
	char *pos;

	pos = strrchr(path,'/');

	if(strcmp(pos+1,".") == 0 || strcmp(pos+1,"..") == 0)
		return 0;
	return 1;
}

int64_t mydu(const char *path)
{
	int i;
	struct stat statres;
	char nextpath[PATHSIZE];
	glob_t globres;
	int64_t sum = 0;

	if(lstat(path,&statres) < 0)
	{
		perror("lstat()");
		exit(1);
	}

	// not a dir
	if(!S_ISDIR(statres.st_mode))
		return statres.st_blocks;

	// is a dir   path =  "../io"  "/etc"  "mydir"

	strncpy(nextpath,path,PATHSIZE);
	strncat(nextpath,"/*",PATHSIZE);
	glob(nextpath,0,NULL,&globres);

	strncpy(nextpath,path,PATHSIZE);
	strncat(nextpath,"/.*",PATHSIZE);
	glob(nextpath,GLOB_APPEND,NULL,&globres);

	for(i = 0 ; i < globres.gl_pathc; i++)
	{
		if(path_no_loop(globres.gl_pathv[i]))
			sum += mydu(globres.gl_pathv[i]);
	}
	sum += statres.st_blocks;
	
	return sum;
}

int main(int argc,char *argv[])
{
	glob_t globres;
	int i,err;

	if(argc < 2)
	{
		fprintf(stderr,"Usage:%s file\n",argv[0]);
		exit(1);
	}

	printf("TOTAL:%lld\n",(long long)mydu(argv[1])/2);

	exit(0);
}



阅读:4.13文件截断truncate()

面试题:写一个程序,功能:删除一个文本文件的第10行

伪码:

fd1 = open(r);
fd2 = open(r+);

len (10);
len(total)

seek->fd1->11
seek->fd2->10

while()
{
	read(fd1)
	write(fd2);

}

truncate(total-10);

代码演示

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

#define PATH	"srcfile.log"
#define SIZE	128

int main()
{
	FILE *fd1,*fd2;
	off_t total;
	char buf[SIZE];
	char buff[SIZE];
	size_t len=0;
	int i=0,sum=0;
	
	fd1 = fopen(PATH,"r");
	fd2 = fopen(PATH,"r+");
	
	fseek(fd1,0,SEEK_END);
	total = ftell(fd1);
	rewind(fd1);

	while(i<10)
	{
		if(fgets(buf,SIZE,fd2) == NULL)
				break;
		sum += strlen(buf);//保存前十行
		i++;
	}
	if(i<10)
	{
		printf("不足10行\n");
		return -1;
	}
	fseek(fd1,sum,SEEK_SET);//第十一行
	fseek(fd2,-strlen(buf),SEEK_CUR);//第十行行首
	
	while(fgets(buff,SIZE,fd1) != NULL)
	{	
		fputs(buff,fd2);
	}
	truncate(PATH,total-strlen(buf));
}

二、glob()

描述:用来匹配通配符指定模式的文本值

手册man 3 glob

头文件

#include <glob.h>

函数原型

int glob(const char *pattern, int flags,
                int (*errfunc) (const char *epath, int eerrno),
                glob_t *pglob);
void globfree(glob_t *pglob);

  • pattern – 要解析的通配符

  • flags – 特殊要求(没有要求填0)

  • int (*errfunc) (const char *epath, int eerrno) – 自己构建函数,说明出错的详细信息(不需要填NULL)

  • pglob – 存放结果的空间

出错信息用法

int myerrfun(const char *epath, int eerrno)
{
	fprintf(stderr,"%s:%s\n",epath,strerror(eerrno);
}

glob_t结构体

typedef struct 
{
    size_t   gl_pathc;    /* Count of paths matched so far  */
    char   **gl_pathv;    /* List of matched pathnames.  */
    size_t   gl_offs;     /* Slots to reserve in gl_pathv.  */
} glob_t
成员含义
size_t gl_pathc计数器(类似man函数的argc)
char **gl_pathv存放结果的数组(类似man函数的argv)

常用特殊要求

GLOB_APPEND(追加)

GLOB_NOCHACK(不检查文件名是否存在,直接将字符串存入结构体)

返回值

成功:0

失败:非零(各种宏)

GLOB_NOSPACE
	for running out of memory,
GLOB_ABORTED
	for a read error, and
GLOB_NOMATCH
	for no found matches.

代码演示

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

#define PAT		"/etc/a*.conf"

int myerrfun(const char *epath, int eerrno)
{
	fprintf(stderr,"%s:%s\n",epath,strerror(eerrno);
}

int main()
{
	glob_t globres;
	int i,err;

	err = glob(PAT, 0, NULL/*myerrfun*/, &globres);
	if(err)
	{
		printf("ERROR CODE:%d\n",err);
		exit(1);
	}

	for(i = 0 ; i < globres.gl_pathc ; i++)
	{
		puts(globres.gl_pathv[i]);
	}
	
	globfree(&globres);
	exit(0);
}

三、opendir()

描述:打开一个目录

手册man 3 opendir

头文件

#include <sys/types.h>
#include <dirent.h>

函数原型

DIR *opendir(const char *name);
  • name – 目录路径名

返回值

成功:返回目录指针

失败:空指针,设置errno

代码演示

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

#define PATH	"/etc"

int main()
{
	DIR *dp;
	struct dirent *cur;

	dp = opendir(PATH);
	if(dp == NULL)
	{
		perror("opendir()");
		exit(1);
	}

	while((cur = readdir(dp)) != NULL)
	{
		puts(cur->d_name);
	}

	closedir(dp);
	exit(0);
}

四、getcwd()

描述:会将当前工作目录的绝对路径复制到参数buf所指的内存空间中,参数sizebuf的空间大小

手册man getcwd

头文件

#include <unistd.h>

函数原型

char *getcwd(char *buf, size_t size);
  • buf – 所要存绝对路径的数组空间

  • size – 数组的大小

返回值

成功:返回当前工作目录绝对路径

失败:返回 NULL,设置errno

五、getpwuid() & getpwnam()

描述:将文件或目录拥有者的uid号拥有者名字传入函数中,将其信息保存到结构体passwd

手册man getpwuid

头文件

#include <sys/types.h>
#include <pwd.h>

函数原型

struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);
  • name – 文件或目录拥有者的名字
  • uid – 文件或目录拥有者uid号

返回值

成功:passwd结构体指针

失败:空指针

结构体

struct passwd 
{
               char   *pw_name;       /* username */
               char   *pw_passwd;     /* user password */
               uid_t   pw_uid;        /* user ID */
               gid_t   pw_gid;        /* group ID */
               char   *pw_gecos;      /* user information */
               char   *pw_dir;        /* home directory */
               char   *pw_shell;      /* shell program */
};

六、getgrgid() & getgrnam()

描述:将文件或目录所属组的gid号组名传入函数中,将其信息保存到结构体group

手册man getgrgid

头文件

#include <sys/types.h>
#include <grp.h>

函数原型

struct group *getgrnam(const char *name);
struct group *getgrgid(gid_t gid);
  • name – 文件或目录所属的组名
  • gid – 文件或目录的gid号

返回值

成功:group结构体指针

失败:空指针

结构体

struct group 
{
               char   *gr_name;        /* group name */
               char   *gr_passwd;      /* group password */
               gid_t   gr_gid;         /* group ID */
               char  **gr_mem;         /* NULL-terminated array of pointers
                                          to names of group members */
};

代码演示

功能:将uid转换拥有者名。

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

int main(int argc,char *argv[])
{
	struct passwd *pwdline;

	if(argc < 2)
	{
		fprintf(stderr,"Usage:%s file\n",argv[0]);
		exit(1);
	}
	
	
	pwdline = getpwuid(atoi(argv[1]));//atoi():将字符串转化成整型,返回对应uid的结构体
	if(pwdline == NULL)
	{
		perror("getpwuid()");
		exit(1);
	}

	puts(pwdline->pw_name);

	exit(0);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一个关于嵌入式系统很详细的文档 第1章 概 述 1.1 研发背景 随着电子技术的发展,微型计算机应用迅速渗透到各个领域。工业控制领域中计算机应用从编程器、单板机、微机系统到工业网络,由简单到复杂,对软件的要求越来越高。近年来网络技术的飞速发展,又为工业自动化开辟了广阔的发展空间,用户可以方便快捷地组建优质高效的监控系统,并且通过采用远程监控及诊断、双机热备等先进技术,使系统更加安全可靠,在这方面,出现了许多应用软件,其中的典型代表就是工控组态软件,如昆仑通态软件公司的MCGS,亚控公司的组态王,Intellution公司的!FIX等。 组态软件的目的就是提供给用户一种二次开发平台,尽量减少组建工程过程中的重复劳动,隐蔽复杂的编程过程。而目前的组态软件大多停留在对人机界面的组态上,随着我国工业自动化水平的不断提高和普及,对工控组态软件的要求也越来越高。对设备驱动程序的可组态是目前研究的热点。 一直以来,工业控制软件缺乏统一的工业标准,不同厂家的软件之间进行通讯是非常麻烦的一件事。各个厂家的软件对控制系统硬件操作的设备驱动程序接口也各不相同,这样为了对市场上不同厂家的设备都能管理,控制软件厂家必须针对市场上的几百种常用的设备都开发设备驱动程序,而且当硬件设备升级、修改时,驱动程序也必须修改,这样做的代价无疑是相当高的。目前并没有一种软件能够很好的实现对设备驱动的所见即所得的支持[1]。出现这种现象的原因主要是外部设备种类繁多,所基于的协议也是千差万别,如果要实现一种比较通用的驱动很不容易。但是我们可以考虑提供一个一般工程人员非常熟悉的开发环境,在这个环境中通过简单易行的操作实现数据的采集和控制。用户只需要关注他们熟知的通讯规程,然后用一种图形化的方法来实现通讯的一个过程,而不需要对程序内部实现有所了解。所以希望通过这种二次开发平台的设计、开发来解决工控软件与大部分的硬件设备数据交换问题。 1.2 本文的目的和意义 本文主要目的是研究面向对象软件开发技术和软件设计模式在工业数据采集软件设计开发中的应用。内容基于一套包含多种智能控制模块的嵌入式实时控制软件,实际上就是一套软PLC软件包,并结合当前工控组态软件实际情况,开发符合国际标准的功能块图(Function Block Diagram?FBD)编程语言[3],即第四代编程语言(4GL语言),以方便用户的组态为目的,使用户不需要编程经验就可组态出优质高效的符合自己需求的工业控制系统,其功能块编译后实际上生成第三代编程语言(3GL语言),而本文要实现的FBD也就是C++的代码生成器。 这种FBD编程语言有着很广泛的应用价值: 首先,普通的工控组态软件要与设备连接,必须有支持所挂设备的驱动程序,运用符合标准的图形化FBD编程模块后,将不需对市场上的几百种常用的设备都开发设备驱动程序,也不需要专门的编程人员,而由用户根据实际需要,应用功能块来搭建完成,打破了组态软件仅限于人机界面的组态局面,实现设备驱动的可组态。例如可移植到北京昆仑通态自动化软件科技有限公司最新推出的嵌入式组态软件??MCGSE中,使其可以用图形化的FBD语言组态设备和策略,将监控和软逻辑集成一体。 其次,运用于嵌入式实时控制软件,可以作为独立的开发环境,与工控机配合使用,形成软PLC。形成一套面向中小型过程应用和设备集成控制的开放型控制系统。满足工控行业从简单到复杂的各种控制应用要求和各种设备过程自动化控制的应用要求,是用来代替硬PLC(可编程控制器)[4] 和各种设备过程控制系统的理想解决方案。 社会的发展以及先进的生产管理理念的不断引入,当今用户不仅要求系统本身能够完成各种常规及先进的控制要求,而且要求通过友好的动画界面显示现场生产的状况。普通的PLC加HMI人机接口界面软件构成的系统在快速逻辑控制应用领域有其优势[1],尤其在断续的生产过程以及各种设备控制应用领域有他的市场并能保持较为适中的系统价格,但在连续复杂的过程控制以及各种针对特殊应用要求的领域和具备先进的生产管理方面满足不了用户的要求,同时系统的可靠性也有待提高。我们通过软PLC来代替普通的PLC,并集成到现有的MCGS人机界面组态软件中不失为解决上述问题的好方法,实现将图形组态与控制器组态相结合。 笔者主要负责其中基于功能流程图的驱动程序软件的设计和开发,以实现设备驱动的可组态,使用户可方便的用FBD来开发设备驱动,针对大多数的工业过程控制应用如:石油化工、电站、钢铁冶炼、食品加工等要求,完成对控制器的控制策略的组态。工程人员只需简单地从功能块库中拖拽所需功能块到组态窗口中并用软接线连接起来就可快速的生成需要的控制策略。使用规范的FBD编程语言来增强昆仑通态的组态软件??MCGS的脚本处理功能和策略功能,提供可视化的I/O设置,简化设备驱动的开发;取代组态软件中原来的设备接口,完成数据采集和数据传送。然后考虑进一步完善界面和加强功能后作为单独的软件系统,形成一套软逻辑控制系统。 1.3 研究方法与思路 目前,在软件中实现与硬件设备的数据交换有以下一些方法: 1.编写特定设备的驱动程序。这种方式是最多和最常见的手段,一般使用VC、VB、Delphi的高级语言进行纯粹的程序开发。这种方法不仅可以开发数据采集程序,还可以编写硬件设备的底层驱动,功能非常强大。但是这种方式对开发人员要求非常高,开发人员必须非常熟练的使用这些开发工具,还要对在不同平台下的驱动设计方法、技巧十分清楚。一般来说,这种开发都是专业的软件开发公司的业务。对于工业控制中的技术人员不适合,并且这样开发出来的产品一般是二进制文件,硬件有了小的改动都需要修改源代码进行重新编译,不能做到很好的复用性。然而,在工业中使用的硬件设备基本结构相同,存在很小的差异,使用这种方法就不太适合。 2.建立统一数据接口标准[6]。如:OPC基金会制定了一套标准的OPC规范。OPC规范基于微软的COM技术,它规范了过程控制和生产自动化软件与用OPC服务器实现的硬件驱动程序之间的接口,并且提供基于工业自动化应用的统一数据传输平台。各个硬件厂商在开发自己设备时只要遵守这个规范,就可以用标准的通讯程序与硬件进行数据交换。但是,这只是提供一个通讯的框架,必须符合才能使用,它的灵活性很差。并且对于很多简单的智能仪表完全没有必要去遵循相对而言庞大的协议。 3.提供一个二次开发的环境。这里有两种方案[8]:一种是提供了I/O采集和数据通讯的动态连接库,然后在协议编辑器中编写通讯协议,最后生成一个工程文件。其中笛卡儿有限公司开发的Omni Server就是其中的代表作。另一种是遵循IEC1131标准的[3]。IEC1131-3标准是PLC程序设计语言的标准,它提供了一种非常灵活的程序开发方法,是一种面向图形的方法。关于IEC1131-3标准将在后面章节作详细介绍。由于基于IEC1131标准设计的程序基本都是PLC或大型控制器的编程软件,主要考虑的是实现硬件的内部功能,并没有将数据通讯的功能作为主要的考虑因素。 根据本课题的特点,通过参考IEC1131-3标准和其他PLC、控制器设计软件,探讨了一种基于功能块图(FBD)的驱动程序开发模式。通过将具体功能和框架分离的设计方法实现功能的易扩充性和跨平台的易移植性。 1.4 主要背景知识及技术 本文主要涉及工业控制领域的相关知识,在此领域中工控组态软件、PLC及其发展动向、IEC1131-3标准、嵌入式软件等是本文密切相关的内容,这些内容之间有着密切的联系,都是完成本文必不可少的。首先从大的角度看,本文属于工控组态软件的范畴,是组态软件在嵌入式系统平台下运行的一种形式,与嵌入式软件是密不可分的。那么,提到嵌入式就不能不提起PLC,PLC是目前在工业自动化控制领域运用非常广泛的一种逻辑器件,正由于它的运用广泛,人们才十分关注他,在实际的生产应用过程中不断发现其使用的不便之处,摸索出解决问题的方法,即用符合IEC1131-3标准的编程规则,来实现复杂的逻辑控制,并考虑用软件来替代硬的PLC,这也是PLC发展的新的动向。相关的内容将在后面章节作详细介绍。 另外,软件设计用到的面向对象软件开发技术和软件设计模式,也将在本文中作较细致的阐述。 1.5 主要研究内容及本文结构安排 课题内容基于一套包含多种智能控制模块的嵌入式实时控制软件,运用面相对象软件设计方法对其中的FBD功能块进行分析设计,参考IEC1131标准,提出适合需要的编程模型并定义需求、进行功能设计和开发。 作为该课题的负责人,笔者的工作主要是: 1. 运用计算机科学技术知识,结合工业自动化控制领域的相关知识,确定课题的范围。 2. 确定功能块图的模型,根据模型设计和开发FBD开发环境的系统框架。 3. 参考类似软件,结合本软件的要求,分析、编写数学“加”功能块的详细需求文档,实现模块的功能。 本文论述了课题的研究思路、工作和取得的研究成果,文章的内容结构安排如下: 本文分为三大部分: 第一部分,在第一章交代本课题的意义、背景和研究方向,使读者能够了解课题的来由和基本研究思路。 第二部分包括两章,在第二章中介绍嵌入式系统的相关知识和概念,并介绍了MCGS嵌入式实时控制软件的特点、软件结构等相关内容,在第三章讨论完成本课题中需要用到的一些知识和相关技术。为后续论文作必要的知识准备。 第三部分是整个软件系统设计和实现,是本文的主体部分。第四章阐述了FBD的设计思想及其实现效果,第五章中文章详细的介绍了系统的整体设计、核心框架的设计并给出了核心类的实现;第六章介绍了数学“加”模块的需求设计与实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值