LCC编译器的源程序分析(2)LCC编译器的预处理

上面已经介绍了C编译器的目标,其实在实现这个目标之前,是经历了很多阶段处理的,其中第一个阶段的处理,就是预处理。预处理的任务是做什么呢?在LCC里预处理主要是把所有包含的头文件和源程序生成一个中间文件,并且把所有宏展开,替换为真实的值。前面介绍的例子源程序,经过预处理后,就会生成下面的代码,如下:

#line 1 "hello.c"
#line 1 "include/stdio.h"

typedef unsigned int size_t;

typedef unsigned short wchar_t;

typedef wchar_t wint_t;
typedef wchar_t wctype_t;

typedef char * va_list;

#line 39 "include/stdio.h"

struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;

extern FILE *_iob;

typedef long fpos_t;

extern FILE (*impiob)[];

int _filbuf(FILE *);
int flsbuf(int, FILE *);

FILE * _fsopen(const char *, const char *, int);

void clearerr(FILE *);
int fclose(FILE *);
int _fcloseall(void);

FILE * fdopen(int, const char *);

int feof(FILE *);
int ferror(FILE *);
int fflush(FILE *);
int fgetc(FILE *);
wchar_t fgetwc(FILE *);
wchar_t getwc(FILE *);

int _fgetchar(void);
int fgetpos(FILE *, fpos_t *);
char * fgets(char *, int, FILE *);

int fileno(FILE *);

int _flushall(void);
FILE * fopen(const char *, const char *);
int fprintf(FILE *, const char *, …);
int xfprintf(FILE *,const char *,…);
int fputc(int, FILE *);
int _fputchar(int);
int fputs(const char *, FILE *);
size_t fread(void *, size_t, size_t, FILE *);
FILE * freopen(const char *, const char *, FILE *);
int fscanf(FILE *, const char *, …);
int xfscanf(FILE *,const char *,…);
int fsetpos(FILE *, const fpos_t *);
int fseek(FILE *, long, int);
long ftell(FILE *);
size_t fwrite(const void *, size_t, size_t, FILE *);
int getc(FILE *);
int getchar(void);
char * gets(char *);
int getw(FILE *);
int _pclose(FILE *);

FILE * popen(const char *, const char *);

int printf(const char *, …);
int xprintf(const char *,…);
int dprintf(const char *, …);
int putc(int, FILE *);
int putchar(int);
int puts(const char *);
int _putw(int, FILE *);
int remove(const char *);
int rename(const char *,const char *);
void rewind(FILE *);
int _rmtmp(void);
int scanf(const char *, …);
int xscanf(const char *,…);
void setbuf(FILE *, char *);
int setvbuf(FILE *, char *, int, size_t);
int _snprintf(char *, size_t, const char *, …);
int sprintf(char *, const char *, …);
int xsprintf(char *, const char *, …);
int snprintf(char *,size_t,const char *,…);
int xsnprintf(char *,size_t,const char *,…);
int sscanf(const char *, const char *, …);
int xsscanf(const char *,const char *,…);
char * _tempnam(char *, char *);
FILE * tmpfile(void);
char * tmpnam(char *);
char *tempnam(char *,char *);
int ungetc(int, FILE *);
int _unlink(const char *);

int vfprintf(FILE *, const char *, va_list);
int vprintf(const char *, va_list);
int _vsnprintf(char *, size_t, const char *, va_list);
int _vsnwprintf(wchar_t *, size_t, const wchar_t *, va_list);
int vsnprintf(char *,size_t,const char *,va_list);
int xvsnprintf(char *,size_t,const char *,va_list);
int xvsnprintf(char *,size_t,const char *,va_list);
int vsprintf(char *, const char *, va_list);
int xvsprintf(char *, const char *, va_list);
void perror(const char *);
void _wperror(const wchar_t *);

#line 2 "hello.c"

int main(void)
{
int nTest1 = 1;
int nTest2 = 2;
int nTest3;
int i;

nTest3 = nTest1 + nTest2;
printf("nTest3 = %d\r\n",nTest3);

for (i = 0; i < 5; i++)
{
printf("%d\r\n",nTest3+i);
}

printf("00:30:28"" ""Apr 07 2007""\r\nhello world\n");
return 0;
}

现在就来分析一下经过预处理的中间文件。
#line 1 "hello.c"
这行告诉编译器要编译的源文件名称和行号。这里是hello.c的第一行。
#line 1 "include/stdio.h"
在hello.c的第一行里包含了一个头文件include/stdio.h,而这个头文件源程序紧跟着后面,直到#line 2 "hello.c"行前,都是包含的头文件源程序。在那行的后面才是hello.c的源程序,可以看到在main函数里的宏定义也被替换掉了。如下:
printf("00:30:28"" ""Apr 07 2007""\r\nhello world\n");
预处理把宏定义TIME变成了"00:30:28"字符串,把宏定义DATE变成了"Apr 07 2007"。这两个宏定义生成的字符串跟编译时间相关,其实就是把当前编译的时间生成字符放到相应的位置。

上面的中间源程序是LCC预处理生成样子,没有删除任何一行,即使是空行也没有删除一行。其实在C里,还有使用#define定义的宏定义,也会替换掉的。因此,在宏定义处理里,没有复杂的计算,只是字符串的替换。在这里没有介绍完整的预处理代码,因为把主要精力放到C编译器去。

C编译器就是接收上面这样的中间文件,进行编译处理的。

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License