c编译器剖析
你可以去看看《高级编译器设计与实现(Steven.S.Muchnick).(中文版)》。
‘贰’ 求一个尽量完整的编译器:词法分析器+语法分析器
在一个模式被匹配之前,词法分析器往往需要超前扫描该词素后面的若干个字符,使用将字符退回输入流的方法,需要移动大量字符的时间,由于 词法分析器是编译期间唯一需要逐一扫描源程序字符的过程,因此它的效率将极大的影响编译器的性能,因此人们发明了双缓冲区的技术。
双缓冲区技术原理如下:
把一个缓冲区分成前后两个部分,每部分能够容纳N(1024/4096)个字符,每次系统读命令读入N个字符到前半部分或者后半部分,如果剩余的不足N个字符,则在最后增加一个不同于其他任何字符的字符,如eof/#,用于标识源文件的结束。缓冲区包括两个指针beginning和forward,在两个指针之间的字符串就是当前的词素。一开始两个指针都指向第一个字符,然后forward向后扫描,直至发现一个匹配的词素为止。如果forward跨过中间标记,则往后半部分读入N个字符。如果forward指针移过最后位置,则向前半部分读入N个字符,且forward指针重新指向开始继续处理过程。为了处理方便在两个部分的最后都增加一个文件结束标识eof。示意图如下:
______________________________________________________________________
|............for......while.... ........................................ |....int i .................................................. ...................| |_______________________________eof|_______________eof________________eof|
| |
beginning forward
下面是双缓冲区的一个c实现:
#include <stdio.h>
#include <string.h>
#define MAXWORD 1000
struct bibuffer
{
char* buffer[2048]; //缓冲区空间
char* beginning,forward; //前向和后向指针
int count; //前向指针记数
} bbuf;
void parse(char c)
{
if(c=' ')
{
memcpy(word[i],beginning,(size_t)(forward-beginning));
i++;
}
else forward++;
}
int main(int argc,char* argv)
{
File* fp;
char* word[MAXWORD];
int i=0;
buffer=new char[2048];
fp=open("test.c","r");
read(fp,buffer,1023);
buffer[1023]='#';
read(fp,buffer+1024,1023);
buffer[2047]='#';
bbuf->buffer=buffer;
bbuf->beginning=bbuf->forward=bbuf->buffer;
bbuf->count=0;
while(1)
{
forward=forward+1;
if(count==1023)
{
read(fp,buffer+1024,1023);
forward++;
//这个函数的具体代码就要和具体的词法分析规则而定,这里假设只识别空格分割的单词
parse(*forward);
}
else if(count>=2048)
{
read(fp,buffer,1023);
forward=bbuf->buffer;
//这个函数的具体代码就要和具体的词法分析规则而定,这里假设只识别空格分割的单词
parse(*forward);
}
else if(count!=1023&&count<2048&&(*forward)='#')
{
break; //词法分析结束
}
}
}
‘叁’ C语言语法分析器
采用递归下降语法分析方法实现
‘肆’ 编译器可是没有思路.或许是我连编译器的本质还没搞懂吧.分为七个部分,词法分析,语法分析等..对吗
一般都是把源码转为elf,a.out,coff,omf之类格式的文件.再用连接器(或者链接器,反正就是linker).连接成程序(就是exe或dll之类的).
也有些是生成汇编代码,再调用汇编器汇编成elf,a.out,coff,omf之类.再连接.
也有些是直接生成exe,dll的.
vc也是先编译成win32格式(也说是COFF格式)的,再连接成exe,dll.
如果你想做编译器,可以去看<<编译原理>>之类的书.
也可以去下载一些开源的编译器代码来学习.
我也一直在学习编译器的.已经有段时间了.但是还是不太清楚.
编译原理看了大半也晕乎乎的,不知道说什么.
‘伍’ 用C语言编写一个词法分析器
还是编译原理里面的词法分析啊?那个工作量就大喽。
‘陆’ 如何在C语言中用宏来判断当前编译器
热心网友
一.
#define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能理解该命令的本质,总是在此处产生一些困惑,在编程时误用该命令,使得程序的运行与预期的目的不一致,或者在读别人写的程序时,把运行结果理解错误,这对 C语言的学习很不利。
1#define命令剖析
1.1 #define的概念
#define命令是C语言中的一个宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。
该命令有两种格式:一种是简单的宏定义,另一种是带参数的宏定义。
(1) 简单的宏定义:
#define <宏名><字符串>
例: #define PI 3.1415926
(2) 带参数的宏定义
#define <宏名> (<参数表>) <宏体>
例: #define A(x) x
一个标识符被宏定义后,该标识符便是一个宏名。这时,在程序中出现的是宏名,在该程序被编译前,先将宏名用被定义的字符串替换,这称为宏替换,替换后才进行编译,宏替换是简单的替换。
1.2 宏替换发生的时机
为了能够真正理解#define的作用,让我们来了解一下对C语言源程序的处理过程。当我们在一个集成的开发环境如Turbo C中将编写好的源程序进行编译时,实际经过了预处理、编译、汇编和连接几个过程,见图1。
‘柒’ C语言程序分析
程序的分析,都从main()函数说起:
main函数开始定义了5个变量a,b,c,d,p;
puts表示输入提示,第一个输入的是a,第二个输入的是b;
do表示循环大括号内的内容;现在进入大括号内部:
输出 :input operation:
getchar()表示输入一个字符,编译器用字符规则去判定;输入的字符复制给p
if 表示判断语句 :如果……
if(p=='+') add(a,b,c):如果输入的符号是加号“+”则执行add(a,b,c)函数。这里面add是函数调用,它代表了一组功能,具体的功能在上面的add里面写得很清楚了。是将a+b的值给c 最后输出c
同理if(p=='-') minus(a,b,c);else :如果输入的符号是“-”,执行minus(a,b,c)函数,具体函数功能见minus(a,b,c)函数,c=a-b 并把c输出
if(p=='*') multiplication(a,b,c);else…………
if(p=='/') div(a,b,d)………………
一个道理。
总结:
这个程序完成的是四则运算,首先输入两个数字,再输入一个运算符,程序会根据输入的运算符进行相应的运算;如果输入的运算符不是加、减、乘、除中的一个,则提示“没有注册这个运算符号”;当输入为0时,程序退出。
‘捌’ c语言程序分析题
f中第一个参数是传值,编译器会复制一份a传递给函数f,复制的临时变量与a是两个不同的变量,它们的地址不同,此后f中的x与main中的变量a不再有任务关联
f的第二个参数是传地值,f中的y与b的地址在内存中是相同的,操作的是同一个地址
‘玖’ 什么是编译器
编译器
编译器是一种特殊的程序,它可以把以特定编程语言写成的程序变为机器可以运行的机器码。我们把一个程序写好,这时我们利用的环境是文本编辑器。这时我程序把程序称为源程序。在此以后程序员可以运行相应的编译器,通过指定需要编译的文件的名称就可以把相应的源文件(通过一个复杂的过程)转化为机器码了。
[编辑]编译器工作方法
首先编译器进行语法分析,也就是要把那些字符串分离出来。然后进行语义分析,就是把各个由语法分析分析出的语法单元的意义搞清楚。最后生成的是目标文件,我们也称为obj文件。再经过链接器的链接就可以生成最后的可执行代码了。有些时候我们需要把多个文件产生的目标文件进行链接,产生最后的代码。我们把一过程称为交叉链接。
一个现代编译器的主要工作流程如下:
* 源程序(source code)→预处理器(preprocessor)→编译器(compiler)→汇编程序(assembler)→目标程序(object code)→连接器(链接器,Linker)→可执行程序(executables)
工作原理
编译是从源代码(通常为高级语言)到能直接被计算机或虚拟机执行的目标代码(通常为低级语言或机器言)。然而,也存在从低级语言到高级语言的编译器,这类编译器中用来从由高级语言生成的低级语言代码重新生成高级语言代码的又被叫做反编译器。也有从一种高级语言生成另一种高级语言的编译器,或者生成一种需要进一步处理的的中间代码的编译器(又叫级联)。
典型的编译器输出是由包含入口点的名字和地址以及外部调用(到不在这个目标文件中的函数调用)的机器代码所组成的目标文件。一组目标文件,不必是同一编译器产生,但使用的编译器必需采用同样的输出格式,可以链接在一起并生成可以由用户直接执行的可执行程序。
编译器种类
编译器可以生成用来在与编译器本身所在的计算机和操作系统(平台)相同的环境下运行的目标代码,这种编译器又叫做“本地”编译器。另外,编译器也可以生成用来在其它平台上运行的目标代码,这种编译器又叫做交叉编译器。交叉编译器在生成新的硬件平台时非常有用。“源码到源码编译器”是指用一种高级语言作为输入,输出也是高级语言的编译器。例如: 自动并行化编译器经常采用一种高级语言作为输入,转换其中的代码,并用并行代码注释对它进行注释(如OpenMP)或者用语言构造进行注释(如FORTRAN的DOALL指令)。
预处理器(preprocessor)
作用是通过代入预定义等程序段将源程序补充完整。
编译器前端(frontend)
前端主要负责解析(parse)输入的源程序,由词法分析器和语法分析器协同工作。词法分析器负责把源程序中的‘单词’(Token)找出来,语法分析器把这些分散的单词按预先定义好的语法组装成有意义的表达式,语句 ,函数等等。 例如“a = b + c;”前端词法分析器看到的是“a, =, b , +, c;”,语法分析器按定义的语法,先把他们组装成表达式“b + c”,再组装成“a = b + c”的语句。 前端还负责语义(semantic checking)的检查,例如检测参与运算的变量是否是同一类型的,简单的错误处理。最终的结果常常是一个抽象的语法树(abstract syntax tree,或 AST),这样后端可以在此基础上进一步优化,处理。
编译器后端(backend)
编译器后端主要负责分析,优化中间代码(Intermediate representation)以及生成机器代码(Code Generation)。
一般说来所有的编译器分析,优化,变型都可以分成两大类: 函数内(intraproceral)还是函数之间(interproceral)进行。很明显,函数间的分析,优化更准确,但需要更长的时间来完成。
编译器分析(compiler analysis)的对象是前端生成并传递过来的中间代码,现代的优化型编译器(optimizing compiler)常常用好几种层次的中间代码来表示程序,高层的中间代码(high level IR)接近输入的源程序的格式,与输入语言相关(language dependent),包含更多的全局性的信息,和源程序的结构;中层的中间代码(middle level IR)与输入语言无关,低层的中间代码(Low level IR)与机器语言类似。 不同的分析,优化发生在最适合的那一层中间代码上。
常见的编译分析有函数调用树(call tree),控制流程图(Control flow graph),以及在此基础上的变量定义-使用,使用-定义链(define-use/use-define or u-d/d-u chain),变量别名分析(alias analysis),指针分析(pointer analysis),数据依赖分析(data dependence analysis)等等。
上述的程序分析结果是编译器优化(compiler optimization)和程序变形(compiler transformation)的前提条件。常见的优化和变新有:函数内嵌(inlining),无用代码删除(Dead code elimination),标准化循环结构(loop normalization),循环体展开(loop unrolling),循环体合并,分裂(loop fusion,loop fission),数组填充(array padding),等等。优化和变形的目的是减少代码的长度,提高内存(memory),缓存(cache)的使用率,减少读写磁盘,访问网络数据的频率。更高级的优化甚至可以把序列化的代码(serial code)变成并行运算,多线程的代码(parallelized,multi-threaded code)。
机器代码的生成是优化变型后的中间代码转换成机器指令的过程。现代编译器主要采用生成汇编代码(assembly code)的策略,而不直接生成二进制的目标代码(binary object code)。即使在代码生成阶段,高级编译器仍然要做很多分析,优化,变形的工作。例如如何分配寄存器(register allocatioin),如何选择合适的机器指令(instruction selection),如何合并几句代码成一句等等。
‘拾’ C语言的语法分析器
先做个LL(1)或者LALR的语法分析器,然后先把教材上的几个LL(1)的例子调通过。然后网上有C语言子集的文法,有人做了转成大小写这样的表述。通过那个的测试就差不多了。。。。其实做语法分析也没多大用 编译器的难点在于语法制导、代码优化之类的,真要做C语言的完整编译器,普通的学生都几乎不可能实现。。。。就不多说了 你可以动手开始做了 如果你有较强的程序设计能力,做个漂亮的LR(1)分析器还是可以的,实在不会就做SLR(1)这样的分析器,如果程序设计能力比较差,建议先做LL(1),那个比较好做。码字不易,望采纳!