C语言KR圣经笔记 6.6 表查询 6.7 typedef

2024-01-31 06:12

本文主要是介绍C语言KR圣经笔记 6.6 表查询 6.7 typedef,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

6.6 表查询

为了说明结构体的更多方面,本节我们来写一个表查询功能包的内部代码。在宏处理器或编译器的符号表管理例程中,这个代码是很典型的。例如,考虑 #define 语句,当遇到如下行

#define IN 1

时,名称 IN 与其对应的替换文本 1 都要存到一张表中。然后,当名称 IN 出现在如下语句中时,

state = IN;

它必须被替换成 1。

有两个例程用来操纵名称及其替换文本。install(s, t) 将名称 s 和 替换文本 t 记录到表格中,s 和 t 仅仅只是字符串。lookup(s) 在表格中搜索 s,并在找到时返回其指向的指针,若没找到则返回 NULL。

算法是哈希(hash)搜索——传入的名称被转换成一个小的非负整数,后续会用作指针数组的下标索引。一个数组元素指向一个链表的开头,该链表中所有元素名称的哈希值都等于该数组元素的下标值。如果没有一个名称的哈希值等于某个下标值,则该下标对应的数组元素值为NULL。

链表中的块(元素)是一个结构体,包含了:名称的指针,替换文本的指针,链表中下一个块的指针。若下一个指针为空,则标记链表结尾。

struct nlist {        /* 表项 */struct nlist * next;    /* 链表中的下一项 */char *name;             /* 定义的名称 */char *defn;             /* 替换文本 */
};

而指针数组就是

#define HASHSIZE 101
static struct nlist *hastab[HASHSIZE];    /* 指针表 */

lookup 和 install 都要使用的 hash 函数,会把字符串中每个字符值都加到对之前字符打散组合得到的值上,并返回对数组大小取模后得到的余数。这不是最好的hash函数,但是简短而高效。

/* hash:对字符串s生成哈希值 */
unsigned hash(char *s)
{unsigned hashval;for (hashval = 0; *s != '\0'; s++)hasval = *s + 31 * hashval;return hashval % HASHSIZE;
}

无符号算术运算保证哈希值为非负。

哈希过程在数组 hashtab 中生成了一个起始的下标索引;如果字符串能被找到,它会位于从此处开始的链表中。搜索是通过 lookup 来执行的。如果 lookup 发现该项已经存在,则返回指向它的指针,否则返回 NULL。

/* lookup:在hashtab中搜索s */
struct nlist *lookup(char *s)
{struct nlist *np;for (np = hashtab[hash(s)]; np != NULL; np = np->next)if (strcmp(s, np->name) == 0)return np;  /* 找到 */return NULL;        /* 没找到 */
}

lookup 中的 for 循环是遍历链表的标准用法:

for (ptr = head; ptr != NULL; ptr = ptr->next)...

install 使用 lookup 来确定要加入的名称是否已经存在;如果是,则新定义会替换老的。否则,创建一个新的项。如果由于某种原因没有足够的空间来保存新的项,则 install 返回 NULL。

struct nlist *lookup(char *);
char *strdup(char *);/* install:将(name, defn)存入 hashtab 中*/
struct nlist *install(char *name, char *defn)
{struct nlist *np;unsigned hashval;if ((np = lookup(name)) == NULL) {    /* 没找到 */np = (struct nlist *)malloc(sizeof(struct nlist));if (np == NULL || (np->name = strdup(name)) == NULL)return NULL;hashval = hash(name);np->next = hashtab[hashval];hashtab[hasval] = np;} else     /* 已经存在 */free((void *) np->defn);    /* 释放之前的defn */if ((np->defn = strdup(defn)) == NULL)return null;return np;
}

练习6-5、写个程序 undef, 从 lookup 和 install 维护的表中删除一个名称和定义。

练习6-6、基于本节的例程,写出一个适用于 C 程序的简单版本(即不带参数的)的 #define 预处理器。你会发现 getch 和 ungetch 也有用。

6.7 typedef

C语言提供了一种叫做 typdef 的机制,来创建新的数据类型名称。例如,声明

typedef int Length;

使 Length 这个名称成为 int 的同义词。类型 Length 可以用在声明,强制类型转换等等地方,与 int 类型可以使用的地方完全相同:

Length len, maxlen;
Length *lengths[];

类似地,如下声明

typedef char *String;

使 String 成为 char * 或字符指针的同义词,也能用于声明和强制类型转换:

String p, lineptr[MAXLINES], alloc(int);
int strcmp(String, String);
p = (String)malloc(100);

注意,在 typedef 中声明的类型出现在变量名的位置,而不是紧跟在 typedef 单词后面。在语法上,typedef 类似存储类型关键字如 extern, static 等。我们把 typedef 定义的类型名首字母大写,使其更醒目。

举个更复杂的例子,我们对本章前面例子中的树节点使用 typedef:

typedef struct tnode *Treeptr;typedef struct tnode {             /* 树节点: */char *word;            /* 指向文本 */int count;             /* 出现次数 */struct tnode *left;    /* 左子节点 */struct tnode *right;   /* 右子节点 */
} Treenode;

这就创建了两个新的类型关键字: Treenode(结构体)和 Treeptr(结构体指针)。因此 talloc 例程可以写成:

Treeptr talloc(void)
{return (Treeptr) malloc(sizeof(Treenode));
}

必须强调,typedef 声明绝对没有创建新的类型;它仅仅是给某些已经存在的类型加了新的名字。typedef 也没有引入新的语义:用这种方式声明的变量,与不用它而显式写出的变量,两者属性完全相同。实际上, typedef 就像是 #define,区别在于它是由编译器解析的,因此能处理超出预处理器能力的文本替换。例如

typedef int (*PFI)(char *, char *);

创建了 PFI 类型,代表“返回 int 类型的函数的指针(有两个 char * 参数)”,它能用在对应的上下文,例如第五章的排序程序中:

PFI strcmp, numcmp;

除了纯粹的美学问题外,使用 typedef 还有两个主要原因。首先是将程序参数化以应对可移植性问题。如果把 typedef 用于可能依赖机器的数据类型,那么当程序需要移植时,就只有 typedef 需要修改。一种常见的情况是用 typedef 来命名各种不同的整数数量,然后为每种主机选择一套合适的 short、int 和 long。标准库中的 size_t 和 ptrdiff_t 就是这样的例子。

使用 typedef 的第二个目的是为程序提供更好的说明——名为 Treeptr 的类型,会比仅声明为指向复杂结构体类型的指针更好理解。

这篇关于C语言KR圣经笔记 6.6 表查询 6.7 typedef的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/662800

相关文章

MySQL存储过程之循环遍历查询的结果集详解

《MySQL存储过程之循环遍历查询的结果集详解》:本文主要介绍MySQL存储过程之循环遍历查询的结果集,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言1. 表结构2. 存储过程3. 关于存储过程的SQL补充总结前言近来碰到这样一个问题:在生产上导入的数据发现

MySQL JSON 查询中的对象与数组技巧及查询示例

《MySQLJSON查询中的对象与数组技巧及查询示例》MySQL中JSON对象和JSON数组查询的详细介绍及带有WHERE条件的查询示例,本文给大家介绍的非常详细,mysqljson查询示例相关知... 目录jsON 对象查询1. JSON_CONTAINS2. JSON_EXTRACT3. JSON_TA

MYSQL查询结果实现发送给客户端

《MYSQL查询结果实现发送给客户端》:本文主要介绍MYSQL查询结果实现发送给客户端方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录mysql取数据和发数据的流程(边读边发)Sending to clientSending DataLRU(Least Rec

Go语言中泄漏缓冲区的问题解决

《Go语言中泄漏缓冲区的问题解决》缓冲区是一种常见的数据结构,常被用于在不同的并发单元之间传递数据,然而,若缓冲区使用不当,就可能引发泄漏缓冲区问题,本文就来介绍一下问题的解决,感兴趣的可以了解一下... 目录引言泄漏缓冲区的基本概念代码示例:泄漏缓冲区的产生项目场景:Web 服务器中的请求缓冲场景描述代码

Go语言如何判断两张图片的相似度

《Go语言如何判断两张图片的相似度》这篇文章主要为大家详细介绍了Go语言如何中实现判断两张图片的相似度的两种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 在介绍技术细节前,我们先来看看图片对比在哪些场景下可以用得到:图片去重:自动删除重复图片,为存储空间"瘦身"。想象你是一个

Go语言中Recover机制的使用

《Go语言中Recover机制的使用》Go语言的recover机制通过defer函数捕获panic,实现异常恢复与程序稳定性,具有一定的参考价值,感兴趣的可以了解一下... 目录引言Recover 的基本概念基本代码示例简单的 Recover 示例嵌套函数中的 Recover项目场景中的应用Web 服务器中

MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)

《MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)》掌握多表联查(INNERJOIN,LEFTJOIN,RIGHTJOIN,FULLJOIN)和子查询(标量、列、行、表子查询、相关/非相关、... 目录第一部分:多表联查 (JOIN Operations)1. 连接的类型 (JOIN Types)

python编写朋克风格的天气查询程序

《python编写朋克风格的天气查询程序》这篇文章主要为大家详细介绍了一个基于Python的桌面应用程序,使用了tkinter库来创建图形用户界面并通过requests库调用Open-MeteoAPI... 目录工具介绍工具使用说明python脚本内容如何运行脚本工具介绍这个天气查询工具是一个基于 Pyt

MyBatis编写嵌套子查询的动态SQL实践详解

《MyBatis编写嵌套子查询的动态SQL实践详解》在Java生态中,MyBatis作为一款优秀的ORM框架,广泛应用于数据库操作,本文将深入探讨如何在MyBatis中编写嵌套子查询的动态SQL,并结... 目录一、Myhttp://www.chinasem.cnBATis动态SQL的核心优势1. 灵活性与可

Mybatis嵌套子查询动态SQL编写实践

《Mybatis嵌套子查询动态SQL编写实践》:本文主要介绍Mybatis嵌套子查询动态SQL编写方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、实体类1、主类2、子类二、Mapper三、XML四、详解总结前言MyBATis的xml文件编写动态SQL