C专家编程cdecl

时间:2022-09-15 09:23:05

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

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

  1. C专家编程阅读笔记

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

  2. c专家编程摘录

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

  3. &lt&semi;&lt&semi;c专家编程&gt&semi;&gt&semi;笔记

    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. Linux下部署solrCloud

    1. 准备工作 这里我只是把我的师兄教我的关于Solrcloud搭建的过程,以及需要注意的地方文档化了.感谢他教会了我很多. 1.机子IP 三台安装linux系统的机子的IP地址为: 172.24.1 ...

  2. 关于web项目中中文乱码问题的总结

    关于post和get的中文乱码处理 get: (1)转码:String username=request.getParameter("username");       Strin ...

  3. java5后的并发池

    本文可作为传智播客<张孝祥-Java多线程与并发库高级应用>视频的学习记录. 为什么需要并发池 之前写并发的时候 new Thread(new Runnable(){ public voi ...

  4. 在CentOS上配置SAMBA共享目录&lpar;转载&rpar;

    在CentOS上配置SAMBA共享目录 From: https://blog.csdn.net/qiumei1101381170/article/details/53265341 2016年11月21 ...

  5. 增加一台web机注意事项

    2017年4月18日 15:23:57 星期二 增加一台web机时, 先不要挂载进lb 1. 需要将此机器的ip加入到其它服务的白名单内: 数据库, 缓存, 第三方接口等 2. 绑定hosts, 点点 ...

  6. 洛谷UVA12995 Farey Sequence(欧拉函数,线性筛)

    洛谷题目传送门 分数其实就是一个幌子,实际上就是求互质数对的个数(除开一个特例\((1,1)\)).因为保证了\(a<b\),所以我们把要求的东西拆开看,不就是\(\sum_{i=2}^n\ph ...

  7. 快速切题 sgu 111&period;Very simple problem 大数 开平方 难度&colon;0 非java&colon;1

    111.Very simple problem time limit per test: 0.5 sec. memory limit per test: 4096 KB You are given n ...

  8. vim在vps内的终端内支持molokai

    vps的终端内默认的颜色数好像很低.对molokai的支持一直不好. 后查找后得知:vim终端方式默认为16色,而molokai为256配色方案 我以为这是硬件问题,没有解决办法,一直到有一天,我在配 ...

  9. git chekout分支遇到问题:need merge

    解决步骤: 在master上, 1.git add . 2.git commit 3.新建分支,并且checkout到此分支,重新提交

  10. Redis数据类型(上)

    数据类型 1.string(字符串) 2.hash(哈希,类似java里的Map) 3.list(列表) 4.set(集合) 5.zset(sorted set:有序集合) 6.基数 String(字 ...