理解所有分析过程的代码段

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, Brooks/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的更多相关文章

  1. C专家编程阅读笔记

    周末闲来无事,(哗),好久之前买的C专家编程一直没看,翻起来看了一下 尽量不使用unsigned 尽量不要在代码中使用unsigned,尤其是一些看起来是无符号类型的数字,比如年龄等,因为难免要使用u ...

  2. c专家编程摘录

    C专家编程摘录 c操作符的优先级 有时一些c操作符有时并不会像你想象的那样工作. 下方表格将说明这个问题: 优先级问题 表达式 期望的情况 实际情况 . 优先级高于* *p.f (*p).f *(p. ...

  3. <<c专家编程>>笔记

    C专家编程摘录 c操作符的优先级 有时一些c操作符有时并不会像你想象的那样工作. 下方表格将说明这个问题: 优先级问题 表达式 期望的情况 实际情况 . 优先级高于* *p.f (*p).f *(p. ...

  4. C专家编程

    [C专家编程] 1.如果写了这样一条语句: if(3=i).那么编程器会发出“attempted assignment to literal(试图向常数赋值)”的错误信息. 所以将常量放置在==前央, ...

  5. 《C专家编程》数组和指针并不同--多维数组

    <C专家编程>数组和指针并不同 标签(空格分隔): 程序设计论著笔记 1. 背景理解 1.1 区分定义与声明 p83 声明相当于普通声明:它所说明的并不是自身,而是描写叙述其它地方创建的对 ...

  6. C语言学习书籍推荐《C专家编程Expert C Programming Deep C Secrets》下载

    Peter Van Der Linden (作者) <C和C++经典著作 C专家编程Expert C Programming Deep C Secrets>展示了C程序员所使用的编码技巧, ...

  7. 《C专家编程》第三章——分析C语言的声明

    前面一章我们已经说过C语言存在的一些问题和它晦涩的地方,让我们对这门神奇的语言有了更深的了解.现在这一章则集中精力来讨论C语言的声明,分为三块,首先是说明C语言声明晦涩难懂的原因和声明是如何形成的,其 ...

  8. c专家编程读书笔记

    无论在什么时候,如果遇到malloc(strlen(str));,几乎可以直接断定他是错误的,而malloc(strlen(str)+1):才是正确的: 一个L的NUL哟关于结束一个ACSII字符串: ...

  9. 《C专家编程》第四章——令人震惊的事实:数组和指针并不相同

    数组和指针是C语言里相当重要的两部分内容,也是新手程序员最容易搞混的两个地方,本章我们锁定指针与数组,探讨它们的异同点. 首先来看指针与数组在声明上的区别: int a[10]; int *p; 很明 ...

随机推荐

  1. ip netns相关命令

    1.增加虚拟网络命名空间   ip netns add net0   2.显示所有的虚拟网络命名空间 EULER:~ # ip netns list net0 也可通过查看/var/run/netns ...

  2. 【GoLang】GoLang 官方 对 error 处理的意见

    The Go Blog Errors are values 12 January 2015 A common point of discussion among Go programmers, esp ...

  3. AngularJS之Provider, Value, Constant, Service, Factory, Decorator的区别与详解

    本文转载自http://camnpr.com/javascript/1693.html 首先,provider, value, constant, service, factory他们都是provid ...

  4. sharepoint更新左侧列表的名字

    SPWeb myweb = SPContext.Current.Web;            SPList myList = myweb.Lists["nihao"];      ...

  5. poj 3126

    一道搜索的水题,其实搜索相对其他的来说好掌握一点,因为有个固定的模板可以用去套 题目大意就是数字的变化,一个数字只可以变化到它最相邻的一个素数上去,意思是只变化一位数字,求最短的变化方案 #inclu ...

  6. CEF3开发者系列之进程和线程

    CEF3是一个多进程架构框架,如果有了解过chromium的进程架构的,那么就很容易了解CEF3的多进程了.打开CEF3源代码中发布的cefclient实例,如果打开的页面带有flash或者其他插件. ...

  7. codeforces 492B. Vanya and Lanterns 解题报告

    题目链接:http://codeforces.com/problemset/problem/492/B #include <cstdio> #include <cstdlib> ...

  8. WP开发资源

    wp开发:连续两次点击返回键退出程序的设计: http://hi.baidu.com/youngytj/item/6be317719cc371306cc37ce4 X http://www.cnblo ...

  9. 【leetcode】Edit Distance (hard)

    Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2 ...

  10. October 11th 2016 Week 42nd Tuesday

    A friend is one who knows you and loves you just the same. 朋友就是懂你并爱你的人. Leave nothing for tomorrow w ...