可调式性编码意味着把系统分成几个部分,先让程序总体结构运行。只有基本的程序能够运行之后你才能为那些复杂的细节完善、性能调优和算法优化进行编码。
有时候,花点时间把编程问题分解成几个部分往往是解决它的最快办法。
下面给出几个例子:
写散列表
自己以前也写过,记得调试时候好痛苦,那么大的散列,找起数据来一点都不方便。
《C专家编程》中作者叙述他的同事这样写散列表:
他首先让最简单的情况能够运行,就是散列函数总是返回一个0。散列函数如下:
/* hash_file: Placeholder for more sophisticated future
routine */
int hash_filename (char *s){
return 0;
}
调用这个散列函数的代码如下:
/*
* find_file: Locate a previously created file descriptor or make a new one if necessary.
*/
file find_filename (char *s)
{
int hash_value = hash_filename(s);
file f;
for (f = file_hash_table[hash_value]; f != NIL;f=f->flink) {
if (strcmp(f->fname, s) == SAME) {
return f;}}
/* file not found, so make a new one: */
f = allocate_file(s);
f->flink = file_hash_table[hash_value];
file_hash_table[hash_value] = f;
return f;
}
FSM实现cdecl
cdecl是分析C语言的声明并把它们翻译成通俗语言。
有限自动机(FSM)可以用作程序的控制结构。
它的基本思路是用一张表保存所有可能的状态,并列出进入每个状态时可能执行的所有动作,其中最后一个动作就是计算(通常在当前状态和下一次输入字符的基础上,另外再经过一次表查询)下一个应该进入的状态。你从一个“初始状态”开始。在这一过程中,翻译表可能告诉你进入了一个错误的状态,表示一个预期之外的或错误的输入。你不停地在各种状态间转换,直到到达结束状态。
在C语言中,有好几种方法可以用来表达FSM,但他们绝大多数都是基于函数指针数组。一个函数指针数组可以像下面这样声明:
void (*state)[MAX_STATES]();
不过这个例子,只需要一个函数函数指针就够了。在主循环里,程序将调用指针所指向的函数,并循环往复,直到结束函数被调用(到达FSM的终结状态)或遇到一个错误状态。
1. 状态机图,详见《C专家编程》第3章
2. 首先编写代码控制状态的转换。让每个动作程序简单打印一条信息,显示它已被调用。对他进行深入调试。
3. 增加代码处理并分析输入的声明。
完整代码:
View Code
/*
* FSM 实现 cdecl
*/
#include <stdio.h>
#include <string.h>
#include <ctype.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 IDENTIFIER;
}
void gettoken(void) { /* 读取下一个标记到”this“ */
char *p = this.string;
// 略过空白字符
while ((*p = getchar()) == ' ')
;
if (isalnum(*p)) {
// 读入的标识符以A-Z,0-9开头
while (isalnum(*++p = getchar()))
;
ungetc(*p, stdin);
*p = '\0';
this.type = classify_string();
return;
}
this.string[1] = '\0';
this.type = *p;
return;
}
/* 状态函数 */
void initialize(), get_array(), get_params(), get_lparen(), get_ptr_part(),
get_type();
void (*nextstate)(void) = initialize;
int main() {
/* 在不同的状态间切换,直到指针值为NULL */
while (nextstate != NULL)
(*nextstate)();
return 0;
}
void initialize() {
gettoken();
while (this.type != IDENTIFIER) {
push(this);
gettoken();
}
printf("%s is ", this.string);
gettoken();
nextstate = get_array;
}
void get_array() {
nextstate = get_params;
while (this.type == '[') {
printf("array ");
gettoken(); /* a number or ']' */
if (isdigit(this.string[0])) {
printf("0..%d ", atoi(this.string) - 1);
gettoken(); /* read the ']' */
}
gettoken(); /* read next past the ']' */
printf("of ");
nextstate = get_lparen;
}
}
void get_params() {
nextstate = get_lparen;
if (this.type == '(') {
while (this.type != ')') {
gettoken();
}
gettoken();
printf("function returning ");
}
}
/* 左小括号 */
void get_lparen() {
nextstate = get_ptr_part;
if (top >= 0) {
if (stack[top].type == '(') {
pop;
gettoken(); /* read past ')' */
nextstate = get_array;
}
}
}
void get_ptr_part() {
nextstate = get_type;
if (stack[top].type == '*') {
printf("pointer to ");
pop;
nextstate = get_lparen;
} else if (stack[top].type == QUALIFIER) {
printf("%s ", pop.string);
nextstate = get_lparen;
}
}
void get_type() {
nextstate = NULL;
/* process tokens that we stacked while reading to
identifier */
while (top >= 0) {
printf("%s ", pop.string);
}
printf("\n");
}