理解所有分析过程的代码段
Page71(中文版)
你可以轻松地编写一个能够分析C语言的声明并把他们翻译成通俗语言的程序。事实上,为什么不?C语言声明的基本形式已经描述清楚。我们所需要的只是编写一段能够理解声明的形式并能够以图3-3的方式对声明进行分析的代码。为了简单起见,暂且忽略错误处理,而且在处理结构、枚举和联合时只简单地用"struct", "enum"和"union"来代表它们的具体内容。最后,这个程序假定函数的括号内没有参数列表
- 编程挑战
编写一个程序,把C语言的声明翻译成通俗语言
这里有一个设计方案,主要的数据结构是一个堆栈,我们从左向右读取,把各个标记依次压入堆栈,直到读到标识符为止。然后我们继续向右读入一个标记,也就是标识符右边的那个标记。接着,观察标识符左边的那个标记(需要从堆栈中弹出)。数据结构大致如下:
struct token {
char type;
char string[MAXTOKENLEN];
};
/* 保存第一个标识之前的所有标记 */
struct token stack[MAXTOKENS];
/* 保存刚读入的那个标记 */
struct token this;
伪码如下:
实用程序------------
classify_string(字符串分类)
查看当前的标记
通过this.type返回一个值,内容为"type(类型)", "qualifier(限定符)"或"indertifier(标识符)"
gettoken(取标记)
把下一个标记读入this.string
如果是字母数字组合,调用classify_string
否则,它必是一个单字符标记,this.type = 该标记;用一个nul结束this.string
read_to_first_identifier()
调用gettoken,并把标记压入到堆栈中,直到遇见第一个标识符。
Print"identifier is (标识符是)", this.string
继续调用gettoken
解析程序-----------
deal_with_fuction_args(处理函数参数)
当读取越过右括号')'后, 打印"函数返回"
deal_with_arrays(处理函数数组)
当你读取"[size]"后,将其打印并继续向右读取。
deal_with_any_pointers(处理任何指针)
当你从堆栈中读取"*"时, 打印"指向...的指针"并将其弹出堆栈。
deal_with_declarator(处理声明器)
if this.type is '[' deal_with_arrays
if this.type is '(' deal_with_function_args
deal_with_any_pointers
while 堆栈里还有东西
if 它是一个左括号'('
将其弹出堆栈,并调用gettoken; 应该获得右括号'('
deal_with_declarator
else 将其弹出堆栈并打印它
主程序-----------
main
read_to_first_identifier
deal_with_declarator
这是一个小型程序,在过去的几年中已被编写过无数次,通常取名为"cdecl". The C Programming Language有一个cdecl的不完整版本,本书的cdecl程序则更为详尽。它支持类型限定符const和volatile。同时它还涉及结构、枚举和联合,尽管在这方面做了简化。你可以轻松地用这个版本的程序来处理函数中的参数声明。这个程序可以用大约150行C代码实现。如果加入错误吹,并使程序能够处理的声明范围更广一些,程序就会更长一些。无论如何,当编制这个解析器时,相当于正在实现编译器中主要的子系统之一----这是一个相当了不起的编程成就,能够帮助你获得对这个领域的深刻理解。
更多的阅读材料
既然已经精通了在C语言中创建数据结构的方法,可能会对那些讲述通用目的的数据结构书感兴趣。其中一本是Data Structures with Abstract Data Types, Daniel F.Stubb和Neil W.Webre著,第二版,Pacific Grove, CA, *s/Cole, 1989.
这本书覆盖了范围很广的数据结构,包括字符串、列表、堆栈、队列、树、堆、集合和图。我推荐此书。
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#define MAXTOKENS 100
#define MAXTOKENLEN 64
enum type_tag { IDENTIFIER, QUALIFIER, TYPE };
struct token{
char type;
char string[MAXTOKENLEN];
};
int top = -1;
struct token stack[MAXTOKENS];
struct token this;
#define pop stack[top--]
#define push(s) stack[++top] = s
/* 推断标识符的类型 */
enum type_tag classify_string(void)
{
char *s = this.string;
if (strcmp(s, "const") == 0) {
strcpy(s, "read-only");
return QUALIFIER;
}
if (strcmp(s, "volatile") == 0) {
return QUALIFIER;
}
if (strcmp(s, "void") == 0) {
return TYPE;
}
if (strcmp(s, "char") == 0) {
return TYPE;
}
if (strcmp(s, "signed") == 0) {
return TYPE;
}
if (strcmp(s, "unsigned") == 0) {
return TYPE;
}
if (strcmp(s, "short") == 0) {
return TYPE;
}
if (strcmp(s, "int") == 0) {
return TYPE;
}
if (strcmp(s, "long") == 0) {
return TYPE;
}
if (strcmp(s, "float") == 0) {
return TYPE;
}
if (strcmp(s, "double") == 0) {
return TYPE;
}
if (strcmp(s, "struct") == 0) {
return TYPE;
}
if (strcmp(s, "union") == 0) {
return TYPE;
}
if (strcmp(s, "enum") == 0) {
return TYPE;
}
return INDENTIFIER;
}
/* 读取下一个标记到"this" */
void gettoken(void)
{
char *p = this.string;
/* 略过空白字符 */
while((*p = getchar()) == ' ') {
;
}
/* 读入的标识符以A-Z, 0-9开头 */
if (isalnum(*p)) {
while( isalnum(*++p = getchar()) ) {
;
}
ungetc(*p, stdin);
*p = '\0';
this.type = classify_string();
return ;
}
if ( *p == '*' ) {
strcpy(this.string, "pointer to ");
this.type = '*';
return;
}
this.string[1] = '\0';
this.type = *p;
return;
}
/* 理解所有分析过程的代码段 */
void read_to_first_identifer() {
gettoken();
while( this.type != IDENTIFIER ) {
push(this);
gettoken();
}
printf("%s is ", this.string);
gettoken();
}
void deal_with_arrays() {
while( this.type == '[') {
printf("array ");
gettoken(); /* 数字或 ']' */
if (isdigit(this.string[0])) {
printf("0..%d ", atoi(this.string)-1 );
gettoken(); /* 读取']' */
}
gettoken(); /* 读取']'之后的再一个标记 */
printf("of ");
}
}
void deal_with_function_args() {
while(this.type != ')' ) {
gettoken();
}
gettoken();
printf("function returning ");
}
void deal_with_pointers() {
while(stack[top].type == '*') {
printf("%s ", pop.string);
}
}
/* 处理标识符之后可能存在的数组/函数 */
void deal_with_declarator() {
switch(this.type) {
case '[' : deal_with_arrays();
break;
case '(' : deal_with_function_args();
break;
}
deal_with_pointers();
/* 处理在读入到标识符之前压入到堆栈中的符号 */
while(top >= 0) {
if (stack[top].type == '(' ) {
pop;
gettoken(); /* 读取')'之后的符号 */
deal_with_declarator();
}
else {
printf("%s ", pop.string);
}
}
}
int main()
{
/* 将标记压入堆栈中,直到遇见标识符 */
read_to_first_identifier();
deal_with_declarator();
printf("\n");
return 0;
}
C专家编程cdecl的更多相关文章
-
C专家编程阅读笔记
周末闲来无事,(哗),好久之前买的C专家编程一直没看,翻起来看了一下 尽量不使用unsigned 尽量不要在代码中使用unsigned,尤其是一些看起来是无符号类型的数字,比如年龄等,因为难免要使用u ...
-
c专家编程摘录
C专家编程摘录 c操作符的优先级 有时一些c操作符有时并不会像你想象的那样工作. 下方表格将说明这个问题: 优先级问题 表达式 期望的情况 实际情况 . 优先级高于* *p.f (*p).f *(p. ...
-
<;<;c专家编程>;>;笔记
C专家编程摘录 c操作符的优先级 有时一些c操作符有时并不会像你想象的那样工作. 下方表格将说明这个问题: 优先级问题 表达式 期望的情况 实际情况 . 优先级高于* *p.f (*p).f *(p. ...
-
C专家编程
[C专家编程] 1.如果写了这样一条语句: if(3=i).那么编程器会发出“attempted assignment to literal(试图向常数赋值)”的错误信息. 所以将常量放置在==前央, ...
-
《C专家编程》数组和指针并不同--多维数组
<C专家编程>数组和指针并不同 标签(空格分隔): 程序设计论著笔记 1. 背景理解 1.1 区分定义与声明 p83 声明相当于普通声明:它所说明的并不是自身,而是描写叙述其它地方创建的对 ...
-
C语言学习书籍推荐《C专家编程Expert C Programming Deep C Secrets》下载
Peter Van Der Linden (作者) <C和C++经典著作 C专家编程Expert C Programming Deep C Secrets>展示了C程序员所使用的编码技巧, ...
-
《C专家编程》第三章——分析C语言的声明
前面一章我们已经说过C语言存在的一些问题和它晦涩的地方,让我们对这门神奇的语言有了更深的了解.现在这一章则集中精力来讨论C语言的声明,分为三块,首先是说明C语言声明晦涩难懂的原因和声明是如何形成的,其 ...
-
c专家编程读书笔记
无论在什么时候,如果遇到malloc(strlen(str));,几乎可以直接断定他是错误的,而malloc(strlen(str)+1):才是正确的: 一个L的NUL哟关于结束一个ACSII字符串: ...
-
《C专家编程》第四章——令人震惊的事实:数组和指针并不相同
数组和指针是C语言里相当重要的两部分内容,也是新手程序员最容易搞混的两个地方,本章我们锁定指针与数组,探讨它们的异同点. 首先来看指针与数组在声明上的区别: int a[10]; int *p; 很明 ...
随机推荐
-
Linux下部署solrCloud
1. 准备工作 这里我只是把我的师兄教我的关于Solrcloud搭建的过程,以及需要注意的地方文档化了.感谢他教会了我很多. 1.机子IP 三台安装linux系统的机子的IP地址为: 172.24.1 ...
-
关于web项目中中文乱码问题的总结
关于post和get的中文乱码处理 get: (1)转码:String username=request.getParameter("username"); Strin ...
-
java5后的并发池
本文可作为传智播客<张孝祥-Java多线程与并发库高级应用>视频的学习记录. 为什么需要并发池 之前写并发的时候 new Thread(new Runnable(){ public voi ...
-
在CentOS上配置SAMBA共享目录(转载)
在CentOS上配置SAMBA共享目录 From: https://blog.csdn.net/qiumei1101381170/article/details/53265341 2016年11月21 ...
-
增加一台web机注意事项
2017年4月18日 15:23:57 星期二 增加一台web机时, 先不要挂载进lb 1. 需要将此机器的ip加入到其它服务的白名单内: 数据库, 缓存, 第三方接口等 2. 绑定hosts, 点点 ...
-
洛谷UVA12995 Farey Sequence(欧拉函数,线性筛)
洛谷题目传送门 分数其实就是一个幌子,实际上就是求互质数对的个数(除开一个特例\((1,1)\)).因为保证了\(a<b\),所以我们把要求的东西拆开看,不就是\(\sum_{i=2}^n\ph ...
-
快速切题 sgu 111.Very simple problem 大数 开平方 难度:0 非java:1
111.Very simple problem time limit per test: 0.5 sec. memory limit per test: 4096 KB You are given n ...
-
vim在vps内的终端内支持molokai
vps的终端内默认的颜色数好像很低.对molokai的支持一直不好. 后查找后得知:vim终端方式默认为16色,而molokai为256配色方案 我以为这是硬件问题,没有解决办法,一直到有一天,我在配 ...
-
git chekout分支遇到问题:need merge
解决步骤: 在master上, 1.git add . 2.git commit 3.新建分支,并且checkout到此分支,重新提交
-
Redis数据类型(上)
数据类型 1.string(字符串) 2.hash(哈希,类似java里的Map) 3.list(列表) 4.set(集合) 5.zset(sorted set:有序集合) 6.基数 String(字 ...