C专家编程cdecl
理解所有分析过程的代码段
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的更多相关文章
- 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; 很明 ...
随机推荐
- 【实例】html5-canvas中实现背景图片的移动
本例来自于<HTML5 Canvas核心技术 图形.动画与游戏开发> 在线演示 (图有点多,请多刷新几次) 本例还有一点代码不理解,我用注释和问号标注出来了,有大神看到求解答,谢谢 本例子 ...
- C#中使用ADOMD.NET查询多维数据集
ADOMD.NET 是用于与 Microsoft SQL Server Analysis Services 进行通信的 Microsoft .NET Framework 数据访问接口. ADOMD.N ...
- ASP.NET Web API与Rest web api
ASP.NET Web API is a framework that makes it easy to build HTTP services that reach a broad range of ...
- Solr集群更新配置的方式
solr集群中配置文件是经常更新的,频率最高的也就是schema.xml和solrconfig.xml这两个配置文件了,对于更新配置文件之前,我们先了解一下集群项目结构 由于在集群模式下,solrco ...
- Linux下安装gcc和g++
以CentOS为例,安装后是没有C语言和C++编译环境的,需要手动安装,最简单的是用yum的方式安装,过程如下: 1.安装gcc yum install gcc 询问是否,按y键回车即可,或者 yum ...
- ACM/ICPC 之 分治法入门(画图模拟:POJ 2083)
题意:大致就是要求画出这个有规律的Fractal图形了= = 例如 1 对应 X 2 对应 X X X X X 这个题是个理解分治法很典型的例子(详情请参见Code) 分治法:不断缩小规 ...
- ABAP 权限程序
检查用户销售区域与分销渠道的权限 AUTHORITY-CHECK OBJECT 'V_VBAK_VKO' ID 'VKORG' FIELD wa_all-vkorg ID 'V ...
- Effective C++ -----条款38:通过复合塑模出has-a或“根据某物实现出”
复合(composition)的意义和public继承完全不同. 在应用域(application domain),复合意味has-a(有一个).在实现域(implementation domain) ...
- HDU 4966 GGS-DDU(最小树形图)
n个技能,每个技能有0-a[i]的等级,m个课程,每个课程需要前置技能c[i]至少达到lv1[i]等级,效果是技能d[i]达到lv2[i]等级,花费w[i]. 输出最小花费使得全技能满级(初始全技能0 ...
- 用Python套接字创建HTTP客户与服务器程序
最近在学习python,网络编程中,python寥寥几句,就可以创建一个服务端和客户端程序: 服务端: import sockets = socket.socket()host = socket.ge ...