报告部分链接

pl0.c文件

#include <stdio.h>
#include<math.h>
#include "pl0.h"
#include "string.h"/* 解释执行时使用的栈 */
#define stacksize 500int main()
{bool nxtlev[symnum];printf("Input pl/0 file?   ");scanf("%s", fname);     /* 输入文件名 */fin = fopen(fname, "r");if (fin){printf("List object code?(Y/N)");   /* 是否输出虚拟机代码 */scanf("%s", fname);listswitch = (fname[0]=='y' || fname[0]=='Y');printf("List symbol table?(Y/N)");  /* 是否输出名字表 */scanf("%s", fname);tableswitch = (fname[0]=='y' || fname[0]=='Y');fa1 = fopen("fa1.tmp", "w");fprintf(fa1,"Input pl/0 file?   ");fprintf(fa1,"%s\n",fname);//1 initinit();     /* 初始化 */err = 0;cc = cx = ll = 0;ch = ' ';if(-1 != getsym()){  fa = fopen("fa.tmp", "w");fas = fopen("fas.tmp", "w");//2 addsetaddset(nxtlev, declbegsys, statbegsys, symnum);//period英文句号 nxtlev[period] = true;/* 设置声明开始符号集 declbegsys[constsym] = true;declbegsys[varsym] = true;declbegsys[procsym] = true;设置语句开始符号集 statbegsys[beginsym] = true;statbegsys[callsym] = true;statbegsys[ifsym] = true;statbegsys[whilesym] = true;statbegsys[readsym] = true;statbegsys[writesym] = true;*/ //3 blockif(-1 == block(0, 0, nxtlev))   /* 调用编译程序 */{fclose(fa);fclose(fa1);fclose(fas);fclose(fin);printf("\n");return 0;}fclose(fa);fclose(fa1);fclose(fas);if (sym != period){error(9);}if (err == 0){fa2 = fopen("fa2.tmp", "w");//4 interpretinterpret();    /* 调用解释执行程序 */fclose(fa2);}else{printf("Errors in pl/0 program");}}fclose(fin);}else{printf("Can't open file!\n");}printf("\n");return 0;
}/*
* 初始化
*/
void init()
{int i;/* 设置单字符符号 */for (i=0; i<=255; i++){ssym[i] = nul;}//ASCLL码对应元素置为相对应的关键字符号 ssym['+'] = plus;ssym['-'] = minus;ssym['*'] = times;ssym['/'] = slash;ssym['('] = lparen;ssym[')'] = rparen;ssym['='] = eql;ssym[','] = comma;ssym['.'] = period;ssym['#'] = neq;ssym[';'] = semicolon;ssym['%'] = format;   //增加%的关键字符号 /* 设置保留字名字,按照字母顺序,便于折半查找 */strcpy(&(word[0][0]), "arr");  //增加的数组保留字 strcpy(&(word[1][0]), "begin");strcpy(&(word[2][0]), "call");strcpy(&(word[3][0]), "const");strcpy(&(word[4][0]), "do");strcpy(&(word[5][0]), "downint"); //增加的关键字 strcpy(&(word[6][0]), "end");strcpy(&(word[7][0]), "if");strcpy(&(word[8][0]), "odd");strcpy(&(word[9][0]), "procedure");strcpy(&(word[10][0]), "read");strcpy(&(word[11][0]), "then");strcpy(&(word[12][0]), "upint");  //增加的关键字 strcpy(&(word[13][0]), "var");strcpy(&(word[14][0]),"varf");  //增加的关键字 strcpy(&(word[15][0]), "while");strcpy(&(word[16][0]), "write");/* 设置保留字符号 */wsym[0] = arrsym;     //增加的数组保留字符号 wsym[1] = beginsym;wsym[2] = callsym;wsym[3] = constsym;wsym[4] = dosym;wsym[5] = downintsym; //增加的保留字符号 wsym[6] = endsym;wsym[7] = ifsym;wsym[8] = oddsym;wsym[9] = procsym;wsym[10] = readsym;wsym[11] = thensym;wsym[12] = upintsym; //增加的保留字符号 wsym[13] = varsym;wsym[14] = varfsym; //增加的保留字符号 wsym[15] = whilesym;wsym[16] = writesym;/* 设置指令名称 */strcpy(&(mnemonic[lit][0]), "lit");strcpy(&(mnemonic[opr][0]), "opr");strcpy(&(mnemonic[lod][0]), "lod");strcpy(&(mnemonic[sto][0]), "sto");strcpy(&(mnemonic[cal][0]), "cal");strcpy(&(mnemonic[inte][0]), "int");strcpy(&(mnemonic[jmp][0]), "jmp");strcpy(&(mnemonic[jpc][0]), "jpc");strcpy(&(mnemonic[lodow][0]), "lodow");strcpy(&(mnemonic[lodup][0]), "lodup");/* 设置符号集 */for (i=0; i<symnum; i++){declbegsys[i] = false;statbegsys[i] = false;facbegsys[i] = false;}/* 设置声明开始符号集 */declbegsys[constsym] = true;declbegsys[varsym] = true;declbegsys[varfsym]=true;//新增声明开始符号; declbegsys[procsym] = true;declbegsys[arrsym] = true; //新增声明开始符号; /* 设置语句开始符号集 */statbegsys[beginsym] = true;statbegsys[callsym] = true;statbegsys[ifsym] = true;statbegsys[whilesym] = true;statbegsys[readsym] = true;statbegsys[writesym] = true;/* 设置因子开始符号集 */facbegsys[ident] = true;facbegsys[number] = true;facbegsys[lparen] = true;facbegsys[downintsym]=true; //新增添的因子开始符号 facbegsys[upintsym]=true;   //新增添的因子开始符号
}/*
* 用数组实现集合的集合运算
*/
//取
int inset(int e, bool* s)
{return s[e];
}
//并集
int addset(bool* sr, bool* s1, bool* s2, int n)
{int i;for (i=0; i<n; i++){sr[i] = s1[i]||s2[i];}return 0;
}
//差集
int subset(bool* sr, bool* s1, bool* s2, int n)
{int i;for (i=0; i<n; i++){sr[i] = s1[i]&&(!s2[i]);}return 0;
}
//交集
int mulset(bool* sr, bool* s1, bool* s2, int n)
{int i;for (i=0; i<n; i++){sr[i] = s1[i]&&s2[i];}return 0;
}/*
*   出错处理,打印出错位置和错误编码
*/
void error(int n)
{char space[81];memset(space,32,81);space[cc-1]=0; //出错时当前符号已经读完,所以cc-1printf("****%s!%d\n", space, n);fprintf(fa1,"****%s!%d\n", space, n);err++;
}/*
* 漏掉空格,读取一个字符。
*
* 每次读一行,存入line缓冲区,line被getsym取空后再读一行
*
* 被函数getsym调用。
*/
int getch()
{if (cc == ll){if (feof(fin)){printf("program incomplete");return -1;}ll=0;cc=0;printf("%d ", cx);fprintf(fa1,"%d ", cx);ch = ' ';while (ch != 10){//fscanf(fin,"%c", &ch)//richardif (EOF == fscanf(fin,"%c", &ch)){line[ll] = 0;break;}//end richardprintf("%c", ch);fprintf(fa1, "%c", ch);line[ll] = ch;ll++;}printf("\n");fprintf(fa1, "\n");}ch = line[cc];cc++;return 0;
}/*
* 词法分析,获取一个符号
*/
int getsym()
{int i,j,k,k_decimal;/* the original version lacks "\r", thanks to foolevery */while (ch==' ' || ch==10 || ch==13 || ch==9)  /* 忽略空格、换行、回车和TAB */{getchdo;}if (ch>='a' && ch<='z'){           /* 名字或保留字以a..z开头 */k = 0;do {if(k<al){a[k] = ch;k++;}getchdo;} while (ch>='a' && ch<='z' || ch>='0' && ch<='9');a[k] = 0;strcpy(id, a);i = 0;j = norw-1;do {    /* 搜索当前符号是否为保留字 */k = (i+j)/2;if (strcmp(id,word[k]) <= 0){j = k - 1;}if (strcmp(id,word[k]) >= 0){i = k + 1;}} while (i <= j);if (i-1 > j){sym = wsym[k];}else{sym = ident; /* 搜索失败则,是名字或数字 */}}else{if (ch>='0' && ch<='9'){           /* 检测是否为数字:以0..9开头 */k = 0;num = 0;sym = number;do {num = 10*num + ch - '0';k++;getchdo;//处理浮点数 新添模块 if(ch=='.'){k_decimal=0;decimal=0;getchdo;do{if(ch<'0'||ch>'9'){error(40);   //浮点数输入格式错误 }   decimal=10*decimal + ch-'0';k_decimal++;getchdo;}while(ch>='0'&&ch<='9'); num=num+decimal/pow(10,k_decimal);}} while (ch>='0' && ch<='9'); /* 获取数字的值 */k--;if (k > nmax){error(30);}}else{if (ch == ':')      /* 检测赋值符号 */{getchdo;if (ch == '='){sym = becomes;getchdo;}else{sym = nul;  /* 不能识别的符号 */}}else{if (ch == '<')      /* 检测小于或小于等于符号 */{getchdo;if (ch == '='){sym = leq;getchdo;}else{sym = lss;}}else{if (ch=='>')        /* 检测大于或大于等于符号 */{getchdo;if (ch == '='){sym = geq;getchdo;}else{sym = gtr;}}else{sym = ssym[ch];     /* 当符号不满足上述条件时,全部按照单字符符号处理 *///getchdo;//richardif (sym != period){getchdo;}//end richard}}}}}return 0;
}/*
* 生成虚拟机代码
*
* x: instruction.f;
* y: instruction.l;
* z: instruction.a;
*/
int gen(enum fct x, int y, int z ,float z1)
{if (cx >= cxmax){printf("Program too long"); /* 程序过长 */return -1;}code[cx].f = x;code[cx].l = y;code[cx].a = z;code[cx].a1=z1;cx++;return 0;
}/*
* 测试当前符号是否合法
*
* 在某一部分(如一条语句,一个表达式)将要结束时时我们希望下一个符号属于某集?
* (该部分的后跟符号),test负责这项检测,并且负责当检测不通过时的补救措施,
* 程序在需要检测时指定当前需要的符号集合和补救用的集合(如之前未完成部分的后跟
* 符号),以及检测不通过时的错误号。
*
* s1:   我们需要的符号
* s2:   如果不是我们需要的,则需要一个补救用的集?
* n:    错误号
*/
int test(bool* s1, bool* s2, int n)
{if (!inset(sym, s1)){error(n);/* 当检测不通过时,不停获取符号,直到它属于需要的集合或补救的集合 */while ((!inset(sym,s1)) && (!inset(sym,s2))){getsymdo;}}return 0;
}/*
* 编译程序主?
*
* lev:    当前分程序所在层
* tx:     名字表当前尾指针
* fsys:   当前模块后跟符号集?
*/
int block(int lev, int tx, bool* fsys)
{int i;int dx;                 /* 名字分配到的相对地址 */int tx0;                /* 保留初始tx */int cx0;                /* 保留初始cx 虚拟机代码指针*/bool nxtlev[symnum];    /* 在下级函数的参数中,符号集合均为值参,但由于使用数组实现,传递进来的是指针,为防止下级函数改变上级函数的集合,开辟新的空?传递给下级函数*/dx = 3;tx0 = tx;               /* 记录本层名字的初始位置 */table[tx].adr = cx;gendo(jmp, 0, 0,0);if (lev > levmax){error(32);}do {if (sym == constsym)    /* 收到常量声明符号,开始处理常量声明 */{getsymdo;/* the original do...while(sym == ident) is problematic, thanks to calculous *//* do { */constdeclarationdo(&tx, lev, &dx);  /* dx的值会被constdeclaration改变,使用指针 */while (sym == comma){getsymdo;constdeclarationdo(&tx, lev, &dx);}if (sym == semicolon){getsymdo;}else{error(5);   /*漏掉了逗号或者分号*/}/* } while (sym == ident); */}//增添浮点型,浮点型数据可与整型用一个结构 if (sym == varsym)      /* 收到变量声明符号,开始处理变量声明 */{getsymdo;/* the original do...while(sym == ident) is problematic, thanks to calculous *//* do {  */vardeclarationdo(&tx, lev, &dx);while (sym == comma){getsymdo;vardeclarationdo(&tx, lev, &dx);}if (sym == semicolon){getsymdo;}else{error(5);}/* } while (sym == ident);  */}//新添浮点模块 if(sym == varfsym){getsymdo;vardeclarationdo(&tx, lev, &dx);while (sym == comma){getsymdo;vardeclarationdo(&tx, lev, &dx);}if (sym == semicolon){getsymdo;}else{error(5);}}//新添数组声明处理模块 if(sym == arrsym){getsymdo;arrdeclarationdo(&tx,lev,&dx);while (sym == comma){getsymdo;arrdeclarationdo(&tx, lev, &dx);}if (sym == semicolon){getsymdo;}else{error(5);}} while (sym == procsym) /* 收到过程声明符号,开始处理过程声明 */{getsymdo;if (sym == ident){enter(procedur, &tx, lev, &dx); /* 记录过程名字 */getsymdo;}else{error(4);   /* procedure后应为标识符 */}if (sym == semicolon){getsymdo;}else{error(5);   /* 漏掉了分号 */}memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[semicolon] = true;if (-1 == block(lev+1, tx, nxtlev)){return -1;  /* 递归调用 */}if(sym == semicolon){getsymdo;memcpy(nxtlev, statbegsys, sizeof(bool)*symnum);nxtlev[ident] = true;nxtlev[procsym] = true;testdo(nxtlev, fsys, 6);}else{error(5);   /* 漏掉了分号 */}}memcpy(nxtlev, statbegsys, sizeof(bool)*symnum);nxtlev[ident] = true;testdo(nxtlev, declbegsys, 7);} while (inset(sym, declbegsys));   /* 直到没有声明符号 */code[table[tx0].adr].a = cx;    /* 开始生成当前过程代码 */table[tx0].adr = cx;            /* 当前过程代码地址 */table[tx0].size = dx;           /* 声明部分中每增加一条声明都会给dx增加1,声明部分已经结束,dx就是当前过程数据的size */cx0 = cx;gendo(inte, 0, dx,0);             /* 生成分配内存代码 */if (tableswitch)        /* 输出名字表 */{printf("TABLE:\n");if (tx0+1 > tx){printf("    NULL\n");}for (i=tx0+1; i<=tx; i++){switch (table[i].kind){case constant:printf("    %d const %s ", i, table[i].name);printf("val=%d\n", table[i].val);fprintf(fas, "    %d const %s ", i, table[i].name);fprintf(fas, "val=%d\n", table[i].val);break;case variable:printf("    %d varible   %s ", i, table[i].name);printf("lev=%d addr=%d size=1\n", table[i].level, table[i].adr);fprintf(fas, "    %d var   %s ", i, table[i].name);fprintf(fas, "lev=%d addr=%d size=1\n", table[i].level, table[i].adr);break;case procedur:printf("    %d proc  %s ", i, table[i].name);printf("lev=%d addr=%d size=%d\n", table[i].level, table[i].adr, table[i].size);fprintf(fas,"    %d proc  %s ", i, table[i].name);fprintf(fas,"lev=%d addr=%d size=%d\n", table[i].level, table[i].adr, table[i].size);break;case array:printf("    %d array  %s ", i, table[i].name);printf("lev=%d addr=%d size=%d\n", table[i].level, table[i].adr, table[i].size);fprintf(fas,"    %d array  %s ", i, table[i].name);fprintf(fas,"lev=%d addr=%d size=%d\n", table[i].level, table[i].adr, table[i].size);break;}}printf("\n");}/* 语句后跟符号为分号或end */memcpy(nxtlev, fsys, sizeof(bool)*symnum);  /* 每个后跟符号集和都包含上层后跟符号集和,以便补救 */nxtlev[semicolon] = true;nxtlev[endsym] = true;statementdo(nxtlev, &tx, lev);gendo(opr, 0, 0,0);                       /* 每个过程出口都要使用的释放数据段指令 */memset(nxtlev, 0, sizeof(bool)*symnum); /*分程序没有补救集合 */testdo(fsys, nxtlev, 8);                /* 检测后跟符号正确性 */listcode(cx0);                          /* 输出代码 */return 0;
}/*
* 在名字表中加入一项
*
* k:      名字种类const,var or procedure
* ptx:    名字表尾指针的指针,为了可以改变名字表尾指针的值
* lev:    名字所在的层次,,以后所有的lev都是这样
* pdx:    dx为当前应分配的变量的相对地址,分配后要增加1
*/
void enter(enum object k, int* ptx, int lev, int* pdx)
{(*ptx)++;strcpy(table[(*ptx)].name, id); /* 全局变量id中已存有当前名字的名字 */table[(*ptx)].kind = k;switch (k){case constant:  /* 常量名字 */if (num > amax){error(31);  /* 数越界 */num = 0;}table[(*ptx)].val = num;break;case variable:  /* 变量名字 */table[(*ptx)].level = lev;table[(*ptx)].adr = (*pdx);(*pdx)++;break;case procedur:  /* 过程名字 */table[(*ptx)].level = lev;break;case array:table[(*ptx)].level = lev;table[(*ptx)].adr=(*pdx);table[(*ptx)].size=(int)num;(*pdx) = (*pdx) + (int)num;}
}/*
* 查找名字的位置.
* 找到则返回在名字表中的位置,否则返回0.
*
* idt:    要查找的名字
* tx:     当前名字表尾指针
*/
int position(char* idt, int tx)
{int i;strcpy(table[0].name, idt);i = tx;while (strcmp(table[i].name, idt) != 0){i--;}return i;
}/*
* 常量声明处理
*/
int constdeclaration(int* ptx, int lev, int* pdx)
{if (sym == ident){getsymdo;if (sym==eql || sym==becomes){if (sym == becomes){error(1);   /* 把=写成了:= */}getsymdo;if (sym == number){enter(constant, ptx, lev, pdx);getsymdo;}else{error(2);   /* 常量说明=后应是数字 */}}else{error(3);   /* 常量说明标识后应是= */}}else{error(4);   /* const后应是标识 */}return 0;
}/*
* 变量声明处理
*/
int vardeclaration(int* ptx,int lev,int* pdx)
{if (sym == ident){enter(variable, ptx, lev, pdx); // 填写名字表getsymdo;}else{error(4);   /* var后应是标识 */}return 0;
}/*
* 数组声明处理
*/
int arrdeclaration(int* ptx,int lev,int* pdx)
{if (sym == ident){getsymdo;if(sym == lparen){getsymdo;if(sym == number){if(num>0){enter(array, ptx, lev, pdx); // 填写名字表getsymdo;if(sym == rparen){getsymdo;}else{error(47);//数组声明缺少右括号 }}else{error(45);//数组大小输入错误 }}else{error(46);//请输入数组大小}}else{error(48);//数组声明缺少左括号 }}else{error(4);   /* var后应是标识 */}return 0;
}
/*
* 输出目标代码清单
*/
void listcode(int cx0)
{int i;if (listswitch){for (i=0; i<cx; i++){printf("%d %s %d %d\n", i, mnemonic[code[i].f], code[i].l, code[i].a);//printf("%d",code[i].f);fprintf(fa,"%d %s %d %d\n", i, mnemonic[code[i].f], code[i].l, code[i].a);}}
}/*
* 语句处理
*/
int statement(bool* fsys, int* ptx, int lev)
{int i, cx1, cx2;bool nxtlev[symnum];if (sym == ident)   /* 准备按照赋值语句处理 */{i = position(id, *ptx);if (i == 0){error(11);  /* 变量未找到 */}else{if(table[i].kind != variable&&table[i].kind != array){error(12);  /* 赋值语句格式错误 */i = 0;}else{if(table[i].kind == variable){getsymdo;if(sym == becomes){getsymdo;}else{error(13);  /* 没有检测到赋值符号 */}memcpy(nxtlev, fsys, sizeof(bool)*symnum);expressiondo(nxtlev, ptx, lev); /* 处理赋值符号右侧表达式 */if(i != 0){/* expression将执行一系列指令,但最终结果将会保存在栈顶,执行sto命令完成赋值 */gendo(sto, lev-table[i].level, table[i].adr,0);}}//数组元素赋值 else if(table[i].kind == array){getsymdo;if(sym == lparen){getsymdo;if(sym == number){getsymdo;int arrnum=num;if(sym == rparen){getsymdo;if(sym == becomes){getsymdo;}else{error(13);  /* 没有检测到赋值符号 */}memcpy(nxtlev, fsys, sizeof(bool)*symnum);expressiondo(nxtlev, ptx, lev); /* 处理赋值符号右侧表达式 */if(i != 0){/* expression将执行一系列指令,但最终结果将会保存在栈顶,执行sto命令完成赋值 */if(arrnum<table[i].size){gendo(sto, lev-table[i].level, table[i].adr+arrnum,0);}else{error(52);  //数组越界 } }}else{error(51);//数组调用缺少右括号} }else{error(50);//数组调用异常}}else{error(49);//数组调用缺少左括号 } }}}//if (i == 0)}else{if (sym == readsym) /* 准备按照read语句处理 */{getsymdo;if (sym != lparen){error(34);  /* 格式错误,应是左括号 */}else{  //改进代码,使输入输出格式化 do {getsymdo;if(sym==format){if(ch=='d'){getchdo;getsymdo;if(sym==comma){getsymdo;if (sym == ident){i = position(id, *ptx); /* 查找要读的变量 */}else{i=0;}if (i == 0){error(35);  /* read()中应是声明过的变量名 */}else if (table[i].kind != variable&&table[i].kind != array){error(32); /* read()参数表的标识符不是变量, thanks to amd */}else{    //修改增添数组的读入情况 if(table[i].kind == variable){gendo(opr, 0, 16,0);  /* 生成输入指令,读取值到栈顶 */gendo(sto, lev-table[i].level, table[i].adr,0);   /* 储存到变量 */}else if(table[i].kind == array){getsymdo;if(sym == lparen){getsymdo;if(sym == number){getsymdo;int arrnum=num;gendo(opr, 0, 16,0);  /* 生成输入指令,读取值到栈顶 */if(arrnum<table[i].size){gendo(sto, lev-table[i].level, table[i].adr+arrnum,0);   /* 储存到变量 */}else{error(52);//数组越界 } if(sym != rparen){error(51);//数组调用缺少右括号 }}else{error(50);//数组调用异常   }}else{error(49);//数组调用缺少左括号 }  }}getsymdo;}}else if(ch=='f'){getchdo;getsymdo;if(sym==comma){getsymdo;if (sym == ident){i = position(id, *ptx); /* 查找要读的变量 */}else{i=0;}if (i == 0){error(35);  /* read()中应是声明过的变量名 */}else if (table[i].kind != variable&&table[i].kind != array){error(32);    /* read()参数表的标识符不是变量, thanks to amd */}else{//修改增添数组的读入情况 if(table[i].kind == variable){gendo(opr, 0, 17,0);  /* 生成输入指令,读取值到栈顶 */gendo(sto, lev-table[i].level, table[i].adr,0);   /* 储存到变量 */}else if(table[i].kind == array){getsymdo;if(sym == lparen){getsymdo;if(sym == number){getsymdo;int arrnum=num;gendo(opr, 0, 17,0);  /* 生成输入指令,读取值到栈顶 */if(arrnum<table[i].size){gendo(sto, lev-table[i].level, table[i].adr+arrnum,0);   /* 储存到变量 */}else{error(52);}if(sym != rparen){error(51);//数组调用缺少右括号 }}else{error(50);//数组调用异常   }}else{error(49);//数组调用缺少左括号 }  }}getsymdo;}}else{error(41);//格式化字符输入错误 }}else{error(42); //请先输入格式化字符% } } while (sym == comma); /* 一条read语句可读多个变量 */}if(sym != rparen){error(33);  /* 格式错误,应是右括号 */while (!inset(sym, fsys))   /* 出错补救,直到收到上层函数的后跟符号 */{getsymdo;}}else{getsymdo;}}else{if (sym == writesym)    /* 准备按照write语句处理,与read类似 */{getsymdo;if (sym == lparen){   do{getsymdo;if(sym==format){if(ch=='d'){getchdo;getsymdo;if(sym==comma){//do {getsymdo;memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[rparen] = true;nxtlev[comma] = true;       /* write的后跟符号为) or , */expressiondo(nxtlev, ptx, lev); /* 调用表达式处理,此处与read不同,read为给变量赋值 */gendo(opr, 0, 14,0);  /* 生成整数输出指令,输出栈顶的值 *///} while (sym == comma);//if (sym != rparen)//{// error(33);  /* write()中应为完整表达式 *///}//else//{// getsymdo;//}}}else if(ch=='f'){getchdo;getsymdo;if(sym==comma){//do {getsymdo;memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[rparen] = true;nxtlev[comma] = true;       /* write的后跟符号为) or , */expressiondo(nxtlev, ptx, lev); /* 调用表达式处理,此处与read不同,read为给变量赋值 */gendo(opr, 0, 18,0);  /* 生成浮点输出指令,输出栈顶的值 *///} while (sym == comma);//if (sym != rparen)//{//error(33);  /* write()中应为完整表达式 *///}//else//{//getsymdo;//  }}} }}while(sym==comma); if(sym!=rparen){error(33);/* write()中应为完整表达式 */}else{getsymdo;}
//                  do {
//                      getsymdo;
//                      memcpy(nxtlev, fsys, sizeof(bool)*symnum);
//                      nxtlev[rparen] = true;
//                      nxtlev[comma] = true;       /* write的后跟符号为) or , */
//                      expressiondo(nxtlev, ptx, lev); /* 调用表达式处理,此处与read不同,read为给变量赋值 */
//                      gendo(opr, 0, 14,0);  /* 生成输出指令,输出栈顶的值 */
//                  } while (sym == comma);
//                  if (sym != rparen)
//                  {
//                      error(33);  /* write()中应为完整表达式 */
//                  }
//                  else
//                  {
//                      getsymdo;
//                  }}gendo(opr, 0, 15,0);  /* 输出换行 */}else{if (sym == callsym) /* 准备按照call语句处理 */{getsymdo;if (sym != ident){error(14);  /* call后应为标识符 */}else{i = position(id, *ptx);if (i == 0){error(11);  /* 过程未找到 */}else{if (table[i].kind == procedur){gendo(cal, lev-table[i].level, table[i].adr,0);   /* 生成call指令 */}else{error(15);  /* call后标识符应为过程 */}}getsymdo;}}else{if (sym == ifsym)   /* 准备按照if语句处理 */{getsymdo;memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[thensym] = true;nxtlev[dosym] = true;   /* 后跟符号为then或do */conditiondo(nxtlev, ptx, lev); /* 调用条件处理(逻辑运算)函数 */if (sym == thensym){getsymdo;}else{error(16);  /* 缺少then */}cx1 = cx;   /* 保存当前指令地址 */gendo(jpc, 0, 0,0);   /* 生成条件跳转指令,跳转地址未知,暂时写0 */statementdo(fsys, ptx, lev);    /* 处理then后的语句 */code[cx1].a = cx;   /* 经statement处理后,cx为then后语句执行完的位置,它正是前面未定的跳转地址 */}else{if (sym == beginsym)    /* 准备按照复合语句处理 */{getsymdo;memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[semicolon] = true;nxtlev[endsym] = true;  /* 后跟符号为分号或end *//* 循环调用语句处理函数,直到下一个符号不是语句开始符号或收到end */statementdo(nxtlev, ptx, lev);while (inset(sym, statbegsys) || sym==semicolon){if (sym == semicolon){getsymdo;}else{error(10);  /* 缺少分号 */}statementdo(nxtlev, ptx, lev);}if(sym == endsym){getsymdo;}else{error(17);  /* 缺少end或分号 */}}else{if (sym == whilesym)    /* 准备按照while语句处理 */{cx1 = cx;   /* 保存判断条件操作的位置 */getsymdo;memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[dosym] = true;   /* 后跟符号为do */conditiondo(nxtlev, ptx, lev);  /* 调用条件处理 */cx2 = cx;   /* 保存循环体的结束的下一个位置 */gendo(jpc, 0, 0,0);   /* 生成条件跳转,但跳出循环的地址未知 */if (sym == dosym){getsymdo;}else{error(18);  /* 缺少do */}statementdo(fsys, ptx, lev);    /* 循环体 */gendo(jmp, 0, cx1,0); /* 回头重新判断条件 */code[cx2].a = cx;   /* 反填跳出循环的地址,与if类似 */}else{memset(nxtlev, 0, sizeof(bool)*symnum); /* 语句结束无补救集合 */testdo(fsys, nxtlev, 19);   /* 检测语句结束的正确性 */}}}}}}}return 0;
}/*
* 表达式处理
*/
int expression(bool* fsys, int* ptx, int lev)
{enum symbol addop;  /* 用于保存正负号 */bool nxtlev[symnum];if(sym==plus || sym==minus) /* 开头的正负号,此时当前表达式被看作一个正的或负的项 */{addop = sym;    /* 保存开头的正负号 */getsymdo;memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[plus] = true;nxtlev[minus] = true;termdo(nxtlev, ptx, lev);   /* 处理项 */if (addop == minus){gendo(opr,0,1,0); /* 如果开头为负号生成取负指令 */}}else    /* 此时表达式被看作项的加减 */{memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[plus] = true;nxtlev[minus] = true;termdo(nxtlev, ptx, lev);   /* 处理项 */}while (sym==plus || sym==minus){addop = sym;getsymdo;memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[plus] = true;nxtlev[minus] = true;termdo(nxtlev, ptx, lev);   /* 处理项 */if (addop == plus){gendo(opr, 0, 2,0);   /* 生成加法指令 */}else{gendo(opr, 0, 3,0);   /* 生成减法指令 */}}return 0;
}/*
* 项处理
*/
int term(bool* fsys, int* ptx, int lev)
{enum symbol mulop;  /* 用于保存乘除法符号 */bool nxtlev[symnum];memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[times] = true;nxtlev[slash] = true;factordo(nxtlev, ptx, lev); /* 处理因子 */while(sym==times || sym==slash){mulop = sym;getsymdo;factordo(nxtlev, ptx, lev);if(mulop == times){gendo(opr, 0, 4,0);   /* 生成乘法指令 */}else{gendo(opr, 0, 5,0);   /* 生成除法指令 */}}return 0;
}/*
* 因子处理
*/
int factor(bool* fsys, int* ptx, int lev)
{int i;bool nxtlev[symnum];testdo(facbegsys, fsys, 24);    /* 检测因子的开始符号 *//* while(inset(sym, facbegsys)) */  /* 循环直到不是因子开始符号 */if(inset(sym,facbegsys))    /* BUG: 原来的方法var1(var2+var3)会被错误识别为因子 */{  //向下取整 if(sym == downintsym){getsymdo;if(sym == lparen){getsymdo;if(sym == ident)    /* 因子为常量或变量 */{i = position(id, *ptx); /* 查找名字 */if (i == 0){error(11);  /* 标识符未声明 */}else{switch (table[i].kind){case constant:  /* 名字为常量 */gendo(lit, 0, (int)(table[i].val),0);    /* 直接把常量的值入栈 */break;case variable:  /* 名字为变量 */gendo(lodow, lev-table[i].level, table[i].adr,0);   /* 找到变量地址并将其值入栈 */break;case procedur:  /* 名字为过程 */error(21);  /* 不能为过程 */break;}getsymdo;if (sym == rparen){getsymdo;}else{error(22);  /* 缺少右括号 */}}}}else{error(43); //downint后期望出现左括号 }}//向上取整 else if(sym == upintsym){getsymdo;if(sym == lparen){getsymdo;if(sym == ident)    /* 因子为常量或变量 */{i = position(id, *ptx); /* 查找名字 */if (i == 0){error(11);  /* 标识符未声明 */}else{switch (table[i].kind){case constant:  /* 名字为常量 */gendo(lit, 0, (int)(table[i].val)+1,0);    /* 直接把常量的值入栈 */break;case variable:  /* 名字为变量 */gendo(lodup, lev-table[i].level, table[i].adr,0);   /* 找到变量地址并将其值入栈 */break;case procedur:  /* 名字为过程 */error(21);  /* 不能为过程 */break;}getsymdo;if (sym == rparen){getsymdo;}else{error(22);  /* 缺少右括号 */}}}}else{error(44); //upint后期望出现左括号 }}else if(sym == ident)    /* 因子为常量或变量 */{i = position(id, *ptx); /* 查找名字 */if (i == 0){error(11);  /* 标识符未声明 */}else{switch (table[i].kind){case constant:  /* 名字为常量 */gendo(lit, 0, table[i].val,0);    /* 直接把常量的值入栈 */break;case variable:  /* 名字为变量 */gendo(lod, lev-table[i].level, table[i].adr,0);   /* 找到变量地址并将其值入栈 */break;case procedur:  /* 名字为过程 */error(21);  /* 不能为过程 */break;case array:    //增添数组处理模块 getsymdo;if(sym == lparen){getsymdo;if(sym == number){int arrnum=num;getsymdo;if(arrnum<table[i].size){gendo(lod, lev-table[i].level, table[i].adr+arrnum,0);}else{error(52);}if(sym != rparen){error(51);}}else{error(50);} }else{error(49);  }break; }}getsymdo;}else{if(sym == number)   /* 因子为数 */{if (num > amax){error(31);num = 0;}gendo(lit, 0, num,num);getsymdo;}else{if (sym == lparen)  /* 因子为表达式 */{getsymdo;memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[rparen] = true;expressiondo(nxtlev, ptx, lev);if (sym == rparen){getsymdo;}else{error(22);  /* 缺少右括号 */}}testdo(fsys, facbegsys, 23);    /* 因子后有非法符号 */}}}return 0;
}/*
* 条件处理
*/
int condition(bool* fsys, int* ptx, int lev)
{enum symbol relop;bool nxtlev[symnum];if(sym == oddsym)   /* 准备按照odd运算处理 */{getsymdo;expressiondo(fsys, ptx, lev);gendo(opr, 0, 6,0);   /* 生成odd指令 */}else{/* 逻辑表达式处理 */memcpy(nxtlev, fsys, sizeof(bool)*symnum);nxtlev[eql] = true;nxtlev[neq] = true;nxtlev[lss] = true;nxtlev[leq] = true;nxtlev[gtr] = true;nxtlev[geq] = true;expressiondo(nxtlev, ptx, lev);if (sym!=eql && sym!=neq && sym!=lss && sym!=leq && sym!=gtr && sym!=geq){error(20);}else{relop = sym;getsymdo;expressiondo(fsys, ptx, lev);switch (relop){case eql:gendo(opr, 0, 8,0);break;case neq:gendo(opr, 0, 9,0);break;case lss:gendo(opr, 0, 10,0);break;case geq:gendo(opr, 0, 11,0);break;case gtr:gendo(opr, 0, 12,0);break;case leq:gendo(opr, 0, 13,0);break;}}}return 0;
}/*
* 解释程序
*/
void interpret()
{int p, b, t;    /* 指令指针,指令基址,栈顶指针 */struct instruction i;   /* 存放当前指令 */int s[stacksize];   /* 栈 */float s1[stacksize]; printf("start pl0\n");t = 0;b = 0;p = 0;s[0] = s[1] = s[2] = 0;do {i = code[p];    /* 读当前指令 */p++;switch (i.f){case lit:   /* 将a的值取到栈顶 */s[t] = i.a;s1[t]=i.a1;t++;break;case opr:   /* 数学、逻辑运算 */switch ((int)(i.a))  //1变为整型 {case 0:t = b;p = s[t+2];b = s[t+1];break;case 1:s[t-1] = -s[t-1];s1[t-1] = -s1[t-1];break;case 2:t--;s[t-1] = s[t-1]+s[t];s1[t-1] = s1[t-1]+s1[t];break;case 3:t--;s[t-1] = s[t-1]-s[t];s1[t-1] = s1[t-1]-s1[t];break;case 4:t--;s[t-1] = s[t-1]*s[t];s1[t-1] = s1[t-1]*s1[t];break;case 5:t--;s[t-1] = s[t-1]/s[t];s1[t-1] = s1[t-1]/s1[t];break;case 6:s[t-1] = ((int)(s[t-1]))%2;//s1[t-1] = s1[t-1]%2;break;case 8:t--;s[t-1] = (s[t-1] == s[t]);s1[t-1] = (s1[t-1] == s1[t]);break;case 9:t--;s[t-1] = (s[t-1] != s[t]);s1[t-1] = (s1[t-1] != s1[t]);break;case 10:t--;s[t-1] = (s[t-1] < s[t]);s1[t-1] = (s1[t-1] < s1[t]);break;case 11:t--;s[t-1] = (s[t-1] >= s[t]);s1[t-1] = (s1[t-1] >= s1[t]);break;case 12:t--;s[t-1] = (s[t-1] > s[t]);s1[t-1] = (s1[t-1] > s1[t]);break;case 13:t--;s[t-1] = (s[t-1] <= s[t]);s1[t-1] = (s1[t-1] <= s1[t]);break;case 14:    //输出整数 printf("%d ", s[t-1]);fprintf(fa2, "%d", s[t-1]);t--;break;case 15:printf("\n");fprintf(fa2,"\n");break;case 16:       //输入整数 printf("?");fprintf(fa2, "?");scanf("%d", &(s[t]));s1[t]=s[t];fprintf(fa2, "%d\n", s[t]);t++;break;case 17:           //输入浮点数 printf("?");fprintf(fa2, "?");scanf("%f", &(s1[t]));s[t]=s1[t];fprintf(fa2, "%f\n", s1[t]);t++;break;case 18:  //输出浮点数 printf("%f ", s1[t-1]);fprintf(fa2, "%f", s1[t-1]);t--;break;}break;case lod:   /* 取相对当前过程的数据基地址为a的内存的值到栈顶 */s[t] = s[base(i.l,s,b)+(int)(i.a)];//2变为整型s1[t]=s1[base(i.l,s,b)+(int)(i.a)];t++;break;case lodow:   /* 取相对当前过程的数据基地址为a的内存的值向下取整到栈顶 */s[t] = (int)s[base(i.l,s,b)+(int)(i.a)];//2变为整型s1[t]=(int)s1[base(i.l,s,b)+(int)(i.a)];t++;break;case lodup:   /* 取相对当前过程的数据基地址为a的内存的值向上取整到栈顶 */s[t] = (int)s[base(i.l,s,b)+(int)(i.a)]+1;//2变为整型s1[t]=(int)s1[base(i.l,s,b)+(int)(i.a)]+1;t++;break;case sto:   /* 栈顶的值存到相对当前过程的数据基地址为a的内存 */t--;s[base(i.l, s, b) + (int)(i.a)] = s[t];s1[base(i.l, s, b) + (int)(i.a)]=s1[t];break;case cal:   /* 调用子过程 */s[t] = base(i.l, s, b); /* 将父过程基地址入栈 */s[t+1] = b; /* 将本过程基地址入栈,此两项用于base函数 */s[t+2] = p; /* 将当前指令指针入栈 */b = t;  /* 改变基地址指针值为新过程的基地址 */p = i.a;    /* 跳转 */break;case inte:  /* 分配内存 */t += i.a;break;case jmp:   /* 直接跳转 */p = i.a;break;case jpc:   /* 条件跳转 */t--;if(s[t]==s1[t]){if (s[t] == 0){p = i.a;}}else{if(s1[t]==0){p=i.a;}}break;}} while (p != 0);
}/* 通过过程基址求上l层过程的基址 */
int base(int l, int* s, int b)
{int b1;b1 = b;while (l > 0){b1 = (int)s[b1];l--;}return b1;
}

pl0.h

typedef enum {false,true
} bool;#define norw 17     /* 关键字个数 */
#define txmax 100   /* 名字表容量 */
#define nmax 14     /* number的最大位数 */
#define al 10       /* 符号的最大长度 */
#define amax 2047   /* 地址上界*/
#define levmax 3    /* 最大允许过程嵌套声明层数 [0,  levmax]*/
#define cxmax 500   /* 最多的虚拟机代码数 *//* 符号 */
/*新增浮点类型varf 增加格式化字符%(format)*/
/*新增upintsym与downintsym*/
enum symbol {nul,         ident,     number,     plus,      minus,times,       slash,     oddsym,     eql,       neq,lss,         leq,       gtr,        geq,       lparen,rparen,      comma,     semicolon,  period,    becomes,beginsym,    endsym,    ifsym,      thensym,   whilesym,writesym,    readsym,   dosym,      callsym,   constsym,varsym,      procsym,   varfsym,    format,    downintsym,upintsym,    arrsym,
};
#define symnum 37/* 名字表中的类型 */
enum object {constant,variable,procedur,array       //add
};/* 虚拟机代码 jmp l a 跳转到第a条指令int l a,开辟a个栈内存opr l 16 将控制台输入的值读入栈顶sto l a 将栈顶数值,写入由a和l决定的变量的地址lod l a 将由a和l决定的变量的地址内的元素,写入栈顶 lit l a 将数值a放到栈顶opr l 14 将栈顶元素输出到控制台opr l 15 输出换行符opr 0 0 一个过程的虚拟机代码执行完成cal l a 调用子过程,跳转到第a条指令。jpc l a 条件跳转,满足条件,跳转到a条指令。 */enum fct {lit,     opr,     lod,sto,     cal,     inte,jmp,     jpc,     lodow,lodup,
};
#define fctnum 10/* 虚拟机代码结构 */
struct instruction
{enum fct f; /* 虚拟机代码指令 */int l;      /* 引用层与声明层的层次差 */int a;      /* 根据f的不同而不同 */float a1;  /*浮点数使用*/
};FILE* fas;  /* 输出名字表 */
FILE* fa;   /* 输出虚拟机代码 */
FILE* fa1;  /* 输出源文件及其各行对应的首地址 */
FILE* fa2;  /* 输出结果 */
bool listswitch;    /* 显示虚拟机代码与否 */
bool tableswitch;   /* 显示名字表与否 */
char ch;            /* 获取字符的缓冲区,getch 使用 */
enum symbol sym;    /* 当前的符号 */
char id[al+1];      /* 当前ident, 多出的一个字节用于存放0 */
float decimal;
float num;            /* 当前number */int cc, ll;          /* getch使用的计数器,cc表示当前字符(ch)的位置 */
int cx;             /* 虚拟机代码指针, 取值范围[0, cxmax-1]*/
char line[81];      /* 读取行缓冲区 */
char a[al+1];       /* 临时符号, 多出的一个字节用于存放0 */
struct instruction code[cxmax]; /* 存放虚拟机代码的数组 */
char word[norw][al];        /* 保留字 */
enum symbol wsym[norw];     /* 保留字对应的符号值 */
enum symbol ssym[256];      /* 单字符的符号值 */
char mnemonic[fctnum][6];   /* 虚拟机代码指令名称 */
bool declbegsys[symnum];    /* 表示声明开始的符号集合 */
bool statbegsys[symnum];    /* 表示语句开始的符号集合 */
bool facbegsys[symnum];     /* 表示因子开始的符号集合 *//* 名字表结构 */
struct tablestruct
{char name[al];      /* 名字 */enum object kind;   /* 类型:const, var, array or procedure */int val;            /* 数值,仅const使用 */int level;          /* 所处层,仅const不使用 */int adr;            /* 地址,仅const不使用 */int size;           /* 需要分配的数据区空间, 仅procedure使用 */
};struct tablestruct table[txmax]; /* 名字表 */FILE* fin;
FILE* fout;
char fname[al];
int err; /* 错误计数器 *//* 当函数中会发生fatal error时,返回-1告知调用它的函数,最终退出程序 */
#define getsymdo                      if(-1 == getsym()) return -1
#define getchdo                       if(-1 == getch()) return -1
#define testdo(a, b, c)               if(-1 == test(a, b, c)) return -1
#define gendo(a, b, c,d)                if(-1 == gen(a, b, c,d)) return -1
#define expressiondo(a, b, c)         if(-1 == expression(a, b, c)) return -1
#define factordo(a, b, c)             if(-1 == factor(a, b, c)) return -1
#define termdo(a, b, c)               if(-1 == term(a, b, c)) return -1
#define conditiondo(a, b, c)          if(-1 == condition(a, b, c)) return -1
#define statementdo(a, b, c)          if(-1 == statement(a, b, c)) return -1
#define constdeclarationdo(a, b, c)   if(-1 == constdeclaration(a, b, c)) return -1
#define vardeclarationdo(a, b, c)     if(-1 == vardeclaration(a, b, c)) return -1
#define arrdeclarationdo(a,b,c)       if(-1 == arrdeclaration(a,b,c)) return -1 void error(int n);int getsym();
int getch();void init();int gen(enum fct x, int y, int z,float z1);
int test(bool* s1, bool* s2, int n);int inset(int e, bool* s);
int addset(bool* sr, bool* s1, bool* s2, int n);
int subset(bool* sr, bool* s1, bool* s2, int n);
int mulset(bool* sr, bool* s1, bool* s2, int n);
int block(int lev, int tx, bool* fsys);
void interpret();int factor(bool* fsys, int* ptx, int lev);
int term(bool* fsys, int* ptx, int lev);
int condition(bool* fsys, int* ptx, int lev);
int expression(bool* fsys, int* ptx, int lev);
int statement(bool* fsys, int* ptx, int lev);void listcode(int cx0);int vardeclaration(int* ptx, int lev, int* pdx);
int constdeclaration(int* ptx, int lev, int* pdx);
int arrdeclaration(int* ptx,int lev,int *pdx); //新增数组声明处理函数 int position(char* idt, int tx);
void enter(enum object k, int* ptx, int lev, int* pdx);
int base(int l, int* s, int b);

PL0功能扩充(浮点数,数组,数组加减乘除等)相关推荐

  1. 基于C++的PL0语言编译器及功能扩充

    文章目录 一.实验介绍 1. PL0文法说明 2. PL0主函数 3. 词法分析函数 4. 语法语义分析 二.功能扩展 1. 功能扩充后的EBNF表示 2. 功能扩充后的语法表述图 3. 新增部分功能 ...

  2. 请编写fun()函数,函数的功能是删去一维数组中相同的数据在一起出现的数,相同的数只保留一个

    <程序设计基础实训指导教程-c语言> ISBN 978-7-03-032846-5 p143 7.1.2 上级实训内容 [实训内容13]请编写fun()函数,函数的功能是删去一维数组中相同 ...

  3. C语言试题四十七之程序定义了N×M的二维数组,并在主函数中自动赋值。请编写函数function(int a[N][M], int m),该函数的功能是:将数组右上半三角元素中的值乘以m。

    1. 题目 程序定义了N×M的二维数组,并在主函数中自动赋值.请编写函数function(int a[N][M], int m),该函数的功能是:将数组右上半三角元素中的值乘以m. 2 .温馨提示 C ...

  4. C语言试题二十二之定义了3×3的二维数组,并在主函数中赋值。函数的功能使求出数组周边元素的平均值并作为函数值返回给主函数中的s。

    1. 题目 定义了3×3的二维数组,并在主函数中赋值.函数的功能使求出数组周边元素的平均值并作为函数值返回给主函数中的s. 2 .温馨提示 C语言试题汇总里可用于计算机二级C语言笔试.机试.研究生复试 ...

  5. C语言试题二十一之定义n×n的二维数组编写函数 function(int a[][n])功能是:使数组左下半三角元素中的值全部置成0。

    1. 题目 定义了n×n的二维数组,并在主函数中自动赋值.请编写函数 function(int a[][n]),该函数的功能是:使数组左下半三角元素中的值全部置成0. 2 .温馨提示 C语言试题汇总里 ...

  6. Python判断不可变对象(字符串,整数,浮点数,数组)相等的办法以及其底层实现原理

    Python判断不可变对象(字符串,整数,浮点数,数组)相等的办法以及其底层实现原理 Python中,判断不可变对象是否相等与Java十分不同. 结论 Python中判断两个字符串相等:既可以使用&q ...

  7. 输入一个正整数n,再输入n个学生的姓名和百分制成绩,将其转换为两级制成绩后输出。要求定义和调用函数set_grade(stu, n),其功能是根据结构数组stu中存放的学生的百分制成绩score,将其

    输入一个正整数n,再输入n个学生的姓名和百分制成绩,将其转换为两级制成绩后输出.要求定义和调用函数set_grade(stu, n),其功能是根据结构数组stu中存放的学生的百分制成绩score,将其 ...

  8. 多维数组和C#中的数组数组有什么区别?

    C#中的多维数组double[,]和数组数组double[][]什么区别? 如果存在差异,那么每个最好的用途是什么? #1楼 前言:此评论旨在解决okutane提供的答案 ,但是由于SO的愚蠢信誉系统 ...

  9. 【C 语言】指针 与 数组 ( 指针 | 数组 | 指针运算 | 数组访问方式 | 字符串 | 指针数组 | 数组指针 | 多维数组 | 多维指针 | 数组参数 | 函数指针 | 复杂指针解读)

    相关文章链接 : 1.[嵌入式开发]C语言 指针数组 多维数组 2.[嵌入式开发]C语言 命令行参数 函数指针 gdb调试 3.[嵌入式开发]C语言 结构体相关 的 函数 指针 数组 4.[嵌入式开发 ...

最新文章

  1. 微信小程序学习做动画效果
  2. 2021年人工智能全球最具影响力学者榜单AI 2000发布
  3. Linux-CentOS 重置root密码
  4. UVA 617 - Nonstop Travel(数论+暴力枚举)
  5. Dubbo 3.0 预览版解读,6到飞起~
  6. tomcat配置一个线程来调试ThreadLocal的代码问题
  7. containerd设置上网代理
  8. 手机轮廓光怎么拍_摄影技巧:怎么拍影子?手机拍照教程
  9. 狄斯奎诺算法 c语言,图的邻接表实现迪杰斯特拉算法(C语言).doc
  10. Oracle 表数据去重
  11. [SIR数据集实验][2]Java类数据集相应工具使用的小经验
  12. 渗透测试神器CS(4.0)的使用
  13. DAS 2020 Keynote Speech | Adobe 文档分析技术介绍
  14. C# AE axGlobeControl The 3D Analyst extension has not been enabled.
  15. java中 CopyOnWriteArrayList 的使用
  16. CSS盒模型(Box Model)
  17. SM2258XT固态硬盘不认盘量产修复开卡工具,SM2258XT开卡教程
  18. 2023年南开大学税务专硕考研上岸前辈备考经验指导
  19. 友盟用户反馈自定义UI-Android
  20. PostGIS搭建、空间数据库创建

热门文章

  1. 在云计算大数据学习中心学习的第一天
  2. 书记员计算机操作基础知识考试,书记员打字考试怎么考?有哪些形式?
  3. 暴多的教学视频,想要的就快下!
  4. 2015境外人气餐厅榜单!你吃过几家?
  5. JConsole:Java监视与管理控制台
  6. spark task 任务状态管理
  7. 【Sass初级】嵌套选择器规则
  8. 什么是生成器?如何创建生成器
  9. 【C语言】简单猜字游戏
  10. 手机进水声音变小怎么办