编译原理-词法分析器,语法分析器-课程设计

     这是偶然翻出来的一段大学时的代码——词法分析器又称扫描器。词法分析是指将我们编写的文本代码流解析为一个一个的记号,分析得到的记号以供后续语法分析使用。词法分析器的工作是低级别的分析:将字符或者字符序列转化成记号.。在谈论词法分析时,我们使用术语“词法记号”(简称记号)、“模式”和“词法单元”表示特定的含义。
     在分析时,一是把词法分析器当成语法分析的一部分,另一种是把词法分析器当成编译程序的独立部分。在前一种情况下,词法分析器不断地被语法分析器调用,每调用一次词法分析器将从源程序的字符序列拼出一个单词,并将其Token值返回给语法分析器。后一种情况则不同,词法分析器不是被语法分析器不断地调用,而是一次扫描全部单词完成编译器的独立一遍任务。
词法分析器主要特点是不依靠语法,而只依靠词法,即处理一个单词时不依赖于外部单词的信息,因此词法分析器一般都很简单。当然,对某些语言在作词法分析时,在有些情况下不得不往前查看多个字符,有时还要做一些特殊处理,还有一些在词法分析中处理不了的,要留到语法分析中进行处理。

    本算法主要利用状态转换图生成一个词法分析器,对输入的程序进行词法分析,并将分析得到的单词造表。其中关键字表和界限符表的大小是由高级语言的子集决定的,可以用数组装载;而标识符表和常数表的大小取决于输入的待分析程序中的变量、过程名、常数的个数,所以要用指针组成动态链表来装载。当为未了方便,我们也把它定义成数组处理。

   语法分析时,调用词法分析器,根据已知文法利用递归向下分析,检查语法错误。

   本程序为类C编译程序,词法分析时能识别各种关键字、标识符、常数、界符,、算符,能够剔除注释内容(支持单行注释及多行注释),语法分析等分析C语言中的一个小程序,程序体中可以进行变量声明、简单赋值语句、带表达式的复杂赋值语句等内容。

程序在Microsoft Visual C++ 6.0 中运行通过。

/************************************************************************/
/* 使用说明:在程序所在工作目录下建立一个文本文档取名为file.txt,并在其中
输入待分析代码。改程序能分析一简单的C语言程序:头文件、函数。函数体中可包
含变量的定义、声明、赋值语句、条件语句、循环语句等,其中赋值语句中的赋值表
达式可以是任何满足四则混合运算的表达式。

  头文件  :#include < stdio.h >
  #inclide " scanner.c "
  变量声明 :int sum ,num1i,num2i,num ;
  int flag ;
  bool T ;
  赋值语句 :sum = num1i + num2i * (num1i / (num2i + num))
  flag = 1 ;
  T = true ;
  条件语句 :if(num <= 30) { …… }
  if( flag ) { …… }
  循环语句 :while( true ){ …… }
  while( flag ){ …… }
  while( 1 ){ …… }
  while( T ){ …… }
  while( num == 20 ){ …… }
  
	for( num = 0; num < 100; num += 2 ){ …… }
	for( num = 100; num >= 0; num -- ){ …… }
	
	  
		作者  :车水码农
		更新时间 :2009.4.24
		最后更新 :2009.5.27
		更新内容 :增加了条件语句、增量语句,更新了部分函数名,替换部分空语句为reutrn;
		最末错误标记代码143
		最后更新 :2009.5.28
		更新内容 :对代码做了一小部分修改,无关紧要
		最后更新 :2009.5.29
		更新内容 :添加了部分注释,对说明文件做了补充
		最后更新 :2009.5.30
更新内容 :添加了返回值语句,错误代码152                               */
/************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include <ctype.h>
#include<string.h>

FILE *fp;
int Line = 1;
int Valuei;
float Valuef;
char *type;
int err_count=0;

//标识符表
struct id_Table {
	char id_Name[15];//函数、变量名
	char *id_type;//类型(int void)
	char *id_Attrib;//属性(函数,变量?)
	float id_Valuef;
	int id_Valuei;
	int id_Beg;//作用域开始标记
	int id_End;//作用域结束标记
}ID[500];

//常数表
struct num_Table {
	char num[10];//常数串
	char *num_Type;//数据类型
}NUM[100];

char *token="";//存放词法分析时的单词
char *headerFile[10];
int keyCount = 32;//关键字个数
char *key[] = {"auto","break","case","char","const","continue","default",
"do","double","else","enum","extern","float","for",
"goto","if","int","long","register","return","short",
"signed","static","sizeof","struct","switch","typedef","union",
"unsigned","void","volatile","while"};//关键字
int opPoint = 0;
int numPoint = 0;
int headerPoint = 0;
char ch;

//判断空格
int space(char ch) {
	return isspace(ch);
}
//判断字母
int letter(char ch) {
	return isalpha(ch);
}
//判断数字
int digit(char ch) {
	return isdigit(ch);
}
//出错处理
void error(int i,int line,char *str) {
	err_count++;
	printf(" error:%2d, Line: %2d  : %s\n",i,line,str);
	// printf("\n The source file:%d error(s)\n\n\n",err_count);
}
//将读入的单个字符组成单词存放于token中
void concatennation(char ch) {
	int i = strlen(token);
	token[i] = ch;
	token[i+1] = '\0';
}
//查关键字表
int reserve(char *token) {
	for(int i=0;i<keyCount;i++)
	{
		if(!strcmp(token,key[i]))
			return i+1;//是保留字返回其编码
	}
	return 0;//不是返回0
}
//文件指针回退
void retract(FILE *fp,char ch) {
	fseek(fp,-1l,SEEK_CUR);
	ch = ' ';
}
//建表
int buildlist(char *token,int tag) {
	int i =0; int position = -1;
	switch (tag) {
	case 0: //标识符表
		for(i=0;i<opPoint;i++) {
			if(!strcmp(ID[i].id_Name,token))//同一标识符再次出现
				position = i;     //返回其表中位置
		}
		if(-1 == position) {
			strcpy(ID[opPoint].id_Name,token);//否则追加在表尾
			position = opPoint;
			opPoint++;
		}
		break;
	case 1://常数表
		strcpy(NUM[numPoint].num,token);//将常数加入常数表中
		position = numPoint;
		numPoint++;
		break;
	case 2://头文件
		headerFile[headerPoint] = (char *)malloc(15);
		for(i=0;i<headerPoint;i++) {
			if(!strcmp(headerFile[i],token))//假如相等
				position = i;
		}
		strcpy(headerFile[headerPoint],token);
		position = headerPoint;
		headerPoint++;
		break;
	}
	return position;
}

/************************************************************************/
/* 词法分析片段                                                         */
/************************************************************************/
char *scanner(char ch) {
	token=(char *)malloc(15);
	*token = NULL;
	char chNext;
	/*剔除空白字符*/
	while((ch ==' '|| ch == '\t')&&(ch != EOF)) {
		ch=fgetc(fp);
	}
	/************************************************************************/
	/* 头文件名、标识符或保留字                                            */
	/************************************************************************/
	//合法标识符一字母或下划线打头
	if(letter(ch)||(ch=='_')) {
		while(letter(ch) || digit(ch)||(ch =='_')) {
			concatennation(ch);
			ch = fgetc(fp);
		}
		chNext = fgetc(fp);
		//头文件
		if ((ch=='.')&&( (chNext=='h')||(chNext=='c') )) {
			concatennation(ch);
			concatennation(chNext);
			buildlist(token,2);
			return token;
		} else {
			retract(fp,chNext);
			retract(fp,ch);
			if (reserve(token)) { //保留字
				reserve(token);
				return token;
			} else {//标识符
				buildlist(token,0);
				return token;
			}
		}
	}
	/************************************************************************/
	/* 常数数字串                                                          */
	/************************************************************************/
	else if(digit(ch)) { //数字串
		int t = 0;
		while(digit(ch) || (ch == '.')) { //
			if ('.' == ch) {
				t ++ ;
			}
			concatennation(ch);
			ch = fgetc(fp);
		}
		retract(fp,ch);
		buildlist(token,1);
		if (0 == t) {
			NUM[numPoint-1].num_Type = "int";
		} else if (1 == t) {
			NUM[numPoint-1].num_Type = "float";
		} else {
			error(101,Line,"scanner error : Incrementimal point");
		}
		return token;
	}
	/************************************************************************/
	/* 运算符'+'系列                                                       */
	/************************************************************************/
	else if(ch == '+') {
		chNext = fgetc(fp);
		if(chNext == '=') { //赋值运算符
			return "+=";
		} else if(chNext == '+') { //自增运算符
			return "++";
		} else { //双目运算符'+'
			retract(fp,chNext);
			return "+";
		}
	}
	/************************************************************************/
	/* 运算符'-'系列                                                        */
	/************************************************************************/
	else if (ch == '-') {
		chNext = fgetc(fp);
		if (ch == '=') { //赋值运算符
			return "-=";
		} else if(chNext == '-') { //自减运算符
			return "--";
		} else { //双目运算符'-'
			retract(fp,chNext);
			return "-";
		}
	}
	/************************************************************************/
	/* 运算符'*'系列                                                        */
	/************************************************************************/
	else if (ch == '*') {
		chNext = fgetc(fp);
		if(chNext == '=') { //赋值运算符
			return "*=";
		} else {
			retract(fp,chNext);//双目运算符'*'
			return "*";
		}
	}
	/************************************************************************/
	/* 运算符'/'及注释系列                                                  */
	/************************************************************************/
	else if (ch == '/') {
		chNext = fgetc(fp);
		if(chNext == '=') { //赋值运算符
			return "/=";
		} else if(chNext == '/') { //单行注释
			ch = fgetc(fp);
			while (ch!='\n') {
				ch = fgetc(fp);
			}
			scanner(fgetc(fp));
		} else if(chNext == '*') {
			char chh=' ';
			ch = fgetc(fp);
			while((chh!='*')&&(ch!='/')) { //多行注释
				chh = ch;
				ch = fgetc(fp);
			}
			scanner(fgetc(fp));
		} else { //双目运算符'/'
			retract(fp,chNext);
			return "/";
		}
	}
	/************************************************************************/
	/* 赋值运算符、等号                                                     */
	/************************************************************************/
	else if(ch == '=') {
		chNext = fgetc(fp);
		if(chNext == '=') { //等号
			return "==";
		} else { //赋值号
			retract(fp,chNext);
			return "=";
		}
	}
	/************************************************************************/
	/* 小于及小于等于                                                       */
	/************************************************************************/
	else if (ch == '<') {
		chNext = fgetc(fp);
		if(chNext == '=') {
			return "<=";
		} else {
			retract(fp,chNext);
			return "<";
		}
	}
	/************************************************************************/
	/* 大于及大于等于                                                       */
	/************************************************************************/
	else if (ch == '>') {
		chNext = fgetc(fp);
		if(chNext == '=') {
			return ">=";
		} else {
			retract(fp,chNext);
			return ">";
		}
	} else if (ch == '%') {
		return "%";
	}
	/************************************************************************/
	/* 界符'('、')'、'{'、'}'、'['、']'、'"'、'\''、','、';'等              */
	/************************************************************************/
	else if (ch == '(') {
		return "(";
	} else if (ch == ')') {
		return ")";
	} else if (ch == '{') {
		return "{";
	} else if (ch == '}') {
		return "}";
	} else if (ch == '[') {
		return "[";
	} else if (ch == ']') {
		return "]";
	} else if (ch == ',') {
		return ",";
	} else if (ch == ';') {
		return ";";
	} else if (ch == '"') {
		return "\"";
	} else if (ch == '\'') {
		return "'";
	}
	/************************************************************************/
	/* 其他字符                                                             */
	/************************************************************************/
	else if (ch == '\n') {
		return "\n";
	} else if (ch == '#') {
		return "#";
	} else if (ch == '&') {
		return "&";
	} else if (ch == '|') {
		return "|";
	} else if (ch == '^') {
		return "^";
	} else if (ch == '%') {
		return "%";
	} else {
		error(100,Line,"scanner error : ");
		//scanner(fgetc(fp));
	}
	return "chwen";
}

/************************************************************************/
/* 语法分析部分                                                         */
/************************************************************************/
void var_List();

//调用词法分析器获取一个单词
char *getNext() {
	char ch;
	ch = fgetc(fp);
	while (ch != EOF) {
		token = scanner(ch);//获取一个词语
		if (!strcmp(token,"\n")) { //若为换行符则行数加加
			Line++;
			getNext();
		}
		return token;
	}
	return "chwen";
}
//变量定义类型 是返回1,否则返回0
int Type(char *lookhead) {
	if(!strcmp(lookhead,"int")) {
		type = "int";
		return 1;
	} else if (!strcmp(lookhead,"float")) {
		type = "float";
		return 1;
	} else {
		return 0;
	}
}
//函数返回值类型
void re_Type(char*lookhead) {
	if (!strcmp(lookhead,"void")) {
		type = "void";
	} else {
		Type(lookhead);
	}
}
//函数名
void myFunction_Name(char *lookhead) {
	for (int i=0;i<opPoint;i++) {
		if (!strcmp(lookhead,ID[i].id_Name)) {
			ID[i].id_Attrib = "hanshu";
			ID[i].id_Beg = Line;
			ID[i].id_type = type;
		}
	}
}
//变量声明
void id_name() {
	char *lookhead = getNext();
	for (int i=0;i<opPoint;i++) {
		if (!strcmp(lookhead,ID[i].id_Name)) {
			if (i == opPoint-1) {
				ID[i].id_Attrib = "bianliang";
				ID[i].id_Beg = Line;
				ID[i].id_type = type;
			}
		}
	}
}
//变量名 不合法返回-1
int id_name1(char*lookhead) {
	int flag = -1;
	for (int i=0;i<opPoint;i++) {
		if (!strcmp(lookhead,ID[i].id_Name)) {
			flag=i;
			if (ID[i].id_Beg == 0) {
				error(110,Line,"syntax error :variable unDeclareared identifier");
			}
		}
	}
	return flag;
}
//变量长定义
void var_List1() {
	char *lookhead = getNext();
	if (!strcmp(lookhead,",")) {
		var_List();//
	} else if (!strcmp(token,";")) {
		;
	} else {
		error(111,Line,"syntax error : missing ';'");
	}
}
//变量列表
void var_List() {
	id_name();//
	var_List1();//
}
//变量声明
void Declare(char *lookhead) {
	Type(lookhead);//
	var_List(); //
}
//常数 不是常数返回 -1
int Const(char *lookhead) {
	int Flag = -1;
	for (int i=0;i<numPoint;i++) {
		if (!strcmp(lookhead,NUM[i].num)) {
			Flag = i;
			Valuei = atoi(lookhead);
		}
	}
	return Flag;
}
//运算符号 是返回1 否则返回0
int opChar(char *lookhead) {
	if (!strcmp(lookhead,"+")
		|| !strcmp(lookhead,"-")
		|| !strcmp(lookhead,"*")
		|| !strcmp(lookhead,"/")) {
		return 1;
	}
	return 0;
}

void T(char*);
//带括号的表达式
void F(char*lookhead) {
	if (-1 != id_name1(lookhead)) {
		;
	} else if(!strcmp(lookhead,"(")) {
		T(getNext());
		if (strcmp(token,")")) {
			error(112,Line,"syntax error( : missing ')'");
		}
	} else {
		error(113,Line,"syntax error( : missing ')'");
	}
}

void T1() {
	char *lookhead = getNext();
	if (opChar(lookhead)) {
		lookhead = getNext();
		F(lookhead);
		T1();//
	} else if(!strcmp(lookhead,";")) {
		;
	} else {
		error(145,Line,"syntax error : missing ';'");
	}
}
//带括号的加减乘除表达式
void T(char*lookhead) {
	F(lookhead);//
	T1();//
}

void Bdshi(char*lookhead) {
	T(lookhead);//
}

int test_id(char *lookhead) {
	int Flag = -1;
	for (int i=0;i<opPoint;i++) { //该str是不是合法标识符
		if (!strcmp(lookhead,ID[i].id_Name)  && (ID[i].id_Beg!=0)) {
			Flag = i;
		}
	}
	return Flag;
}
//赋值语句
void Fuzhi(char *lookhead,int Flag) {
	lookhead = getNext();
	if (!strcmp(lookhead,"=")) {
		lookhead = getNext();
		if (-1 != Const(lookhead)) { //将常量付给变量
			ID[Flag].id_Valuei = Valuei;
			if(strcmp(getNext(),";")) {
				error(114,Line,"syntax error : missing ';'");
			}
		} else {
			Bdshi(lookhead);//表达式付给变量
		}
	}
}
//条件语句
void Condition() {
	char * lookhead = getNext();
	if(!strcmp(lookhead,"true")||!strcmp(lookhead,"false")||!strcmp(lookhead,"0")||!strcmp(lookhead,"1")) {
		;
	} else if(-1 != id_name1(lookhead)) {
		lookhead = getNext();
		if( (!strcmp(lookhead,"<"))
			||(!strcmp(lookhead,"<="))
			||(!strcmp(lookhead,">"))
			||(!strcmp(lookhead,">="))
			||(!strcmp(lookhead,"=="))
			||(!strcmp(lookhead,"!="))) {
			lookhead = getNext();
			if(-1 == Const(lookhead)) {
				error(140,Line,"syntax error : ')'");
			}
		} else {
			retract(fp,ch);
		}
	} else {
		error(141,Line,"syntax error : ')'");
	}
}
//增量语句
void Increment() {
	char * lookhead = getNext();
	if (-1 != id_name1(lookhead)) {
		lookhead = getNext();
		if ((!strcmp(lookhead,"++"))||(!strcmp(lookhead,"--"))) {
			;
		} else if((!strcmp(lookhead,"+="))
			||(!strcmp(lookhead,"-="))
			||(!strcmp(lookhead,"*="))
			||(!strcmp(lookhead,"/="))) {
			lookhead = getNext();
			if(-1 == Const(lookhead)) {
				error(142,Line,"syntax error : ')'");
			}
		}
	} else {
		retract(fp,ch);
	}
}

void myFunction();

//if 条件语句
void myIf() {
	char *lookhead = getNext();
	if (!strcmp(lookhead,"(")) {
		Condition();
		if (!strcmp(getNext(),")")) {
			if (!strcmp(getNext(),"{")) {
				myFunction();
				retract(fp,ch);
				if (strcmp(getNext(),"}")) {
					error(116,Line,"fatal error : unexpected end of file found");
				}
			} else {
				error(135,Line,"syntax error : missing ';' before '}'");
			}
		} else {
			error(136,Line,"syntax error : missing ')' before '{'");
		}
	} else {
		error(117,Line,"syntax error : missing ';' after 'if'");
	}
}
//while 循环语句
void myWhile() {
	char *lookhead = getNext();
	if (!strcmp(lookhead,"(")) {
		Condition();
		if (!strcmp(getNext(),")")) {
			if (!strcmp(getNext(),"{")) {
				myFunction();
				retract(fp,ch);
				if (strcmp(getNext(),"}")) { //改写
					error(116,Line,"fatal error : unexpected end of file found");
				}
			} else {
				error(137,Line,"syntax error : missing ';' before '}'");
			}
		} else {
			error(138,Line,"syntax error : missing ')' before '{'");
		}
	} else {
		error(117,Line,"syntax error : missing ';' after 'while'");
	}
}
//for 循环语句
void myFor() {
	int i=0;
	char * lookhead = getNext();
	if (!strcmp(lookhead,"(")) {
		lookhead = getNext();
		if (-1 != ( i=test_id(lookhead))) { //初值
			Fuzhi(lookhead,i);
		} else {
			error(131,Line,"unDeclareared identifier");
		}
		Condition();//条件
		lookhead = getNext();
		if (!strcmp(lookhead,";")) {
			Increment();//增量
			lookhead = getNext();
			if (!strcmp(lookhead,")")) {
				if (!strcmp(getNext(),"{")) {
					myFunction();
					retract(fp,ch);
					if (strcmp(getNext(),"}")) {
						error(116,Line,"fatal error : unexpected end of file found");
					}
				} else {
					error(134,Line,"syntax error : missing ';' before '}'");
				}
			} else {
				error(133,Line,"syntax error : missing ')' before '{'");
			}
		} else {
			error(130,Line,"not ';'");
		}
	}
}

void myReturn () {
	char *lookhead = getNext();
	if (!strcmp(lookhead,"0")) {
		lookhead = getNext();
		if (strcmp(lookhead,";")) {
			error(151,Line,"syntax error : missing ';' before '}'");
		}
	} else {
		error(150,Line,"function should return a value; 'void' return type assumed");
	}
}
//函数体语句
void myFunction1(char *str) {
	int i = 0;
	if (Type(str)) {
		Declare(str);
		return;
	} else if (-1 != ( i=test_id(str))) {
		Fuzhi(str,i);
		return;
	} else if (!strcmp(str,"if")) {
		myIf();
		return;
	} else if (!strcmp(str,"while")) {
		myWhile();
		return;
	} else if (!strcmp(str,"for")) {
		myFor();
		return;
	} else if (!strcmp(str,"return")) {
		myReturn();
		return;
	} else {
		;
	}
}
//函数体
void myFunction() {
	char *str  = getNext();
	while (strcmp(str,"}")) {
		myFunction1(str);
		str  = getNext();
	}
}
//参数表
void P() {
	;
}

void headFile() {
	char *lookhead = getNext();
}
//编译预处理头文件
void includ(char*lookhead) {
	if (!strcmp(lookhead,"#")) {
		if (!strcmp(getNext(),"include")) {
			if (!strcmp(getNext(),"<") ) {
				headFile();//
				if (strcmp(getNext(),">") ) {
					error(119,Line,"invalid preprocessor command");
				}
			} else {
				error(120,Line,"invalid preprocessor command");
			}
		} else {
			error(121,Line,"invalid preprocessor command");
		}
	} else {
		error(122,Line,"syntax error : missing ';'");
	}
}
//主函数语句
void myMain(char *lookhead) {
	re_Type(lookhead);//1
	
	lookhead = getNext();
	myFunction_Name(lookhead);//2
	
	lookhead = getNext();
	if(!strcmp(lookhead,"(")) {
		P();
		if(!strcmp(getNext(),")")) {
			if(!strcmp( getNext(),"{")) {
				myFunction();//
				retract(fp,ch);
				lookhead = getNext();
				if (!strcmp( lookhead,"}")) {
					ch = fgetc(fp);
					while (EOF != ch) { //处理结束符‘}’后还有其他字符
						if (!(ch==' ')&&!(ch=='\n')&&!(ch=='\t')) {
							error(143,Line,"syntax error : missing ';' before '}'");
						}
						ch = fgetc(fp);
					}
				} else {
					error(123,Line,"fatal error : unexpected end of file found or  missing '}'");
				}
			} else {
				error(124,Line,"syntax error : missing '{'");
			}
		} else {
			error(125,Line,"syntax error : missing ')'");
		}
	} else {
		error(126,Line,"syntax error : missing '('");
	}
}
//一简单程序(头文件、主函数)
int S() {
	char*lookhead = "";
	
	lookhead = getNext();
	while (!strcmp(lookhead,"#")) {
		includ(lookhead);
		lookhead = getNext();
	}
	myMain(lookhead);
	
	return err_count;
}

void tttt(int t) {
	for (int i=0;i<t;i++) {
		printf("\t");
	}
}
//将源程序格式化输出
void print() {
	fseek(fp,0,0);int tt = 0;
	ch = fgetc(fp);
	while(ch != EOF) {
		if(';'==ch) {
			putchar(ch);
			putchar('\n');
			tttt(tt);
		} else if('\n'==ch) {
			;
		} else if ('{'==ch) {
			tt++;
			putchar(ch);
			putchar('\n');
			tttt(tt);
		} else if('}'==ch) {
			tt--;
			printf("\b\b\b\b\b\b\b\b");
			putchar(ch);
			putchar('\n');
			tttt(tt);
		} else if('>'==ch) {
			putchar(ch);
			putchar('\n');
		} else if((' '==ch)||('\t'==ch)) {
			putchar(' ');
			while((' '==ch)||('\t'==ch)) {
				ch = fgetc(fp);
			}
			if(EOF!=ch) {
				retract(fp,ch);
			} else {
				break;
			}
		} else {
			putchar(ch);
		}
		ch = fgetc(fp);
	}
}

void Result() {
	if (!S()) {
		printf("\nThe source file: %d error(s)\n",err_count);
		print();
		printf("\n The source file success!\n\n\n");
	} else {
		printf("\n The source file:%d error(s)\n\n\n",err_count);
	}
}

/************************************************************************/
/* 主测试函数                                                           */
/************************************************************************/
int main() {
	if((fp = fopen("003.txt","rt")) == NULL) {
		printf("cannot open the file!!\n");
		exit(0);
	}
	
	Result();
	
	fclose(fp);
	return 0;
}

转载于:https://my.oschina.net/chwencong/blog/53031

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值