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 的类型,会比仅声明为指向复杂结构体类型的指针更好理解。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序如何实现点击上传图片功能

如下所示,实际需求中常常存在需要点击上传图片的功能,上传前显示边框表面图片显示大小,上传后将图形缩放到边框大小。 实现如下: .wxml <view class="{{img_src==?blank-area:}}" style="width:100%;height:40%;display:flex;align-items: center;jus…

spring框架(一)

1、Spring框架&#xff1a;IoC和AOP 服务端三层开发&#xff1a;表现层、业务层、持久层 ssm, springboot, springcloud(微服务&#xff0c;治理组件) Spring框架是一个流行的Java应用程序框架&#xff0c;它提供了许多功能来简化企业级应用程序的开发。其中&#xff0c;控制反…

Selenium 隐藏浏览器指纹特征的几种方式

我们使用 Selenium 对网页进行爬虫时&#xff0c;如果不做任何处理直接进行爬取&#xff0c;会导致很多特征是暴露的 对一些做了反爬的网站&#xff0c;做了特征检测&#xff0c;用来阻止一些恶意爬虫 本篇文章将介绍几种常用的隐藏浏览器指纹特征的方式 1. 直接爬取 目标对…

面试题 02.07. 链表相交(力扣LeetCode)

文章目录 面试题 02.07. 链表相交题目描述解题思路c代码优化后c代码 面试题 02.07. 链表相交 题目描述 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点&#xff0c;返回 null 。 图示两个链表在节点 c1 …

shell

目录 一.运行方式 二.编程习惯 三.变量 3.1变量的命名 3.3普通变量(局部变量) 3.4特殊变量 3.5变量子串 3.6变量赋值 四.运算方式 4.1$(( )) 4.2let 4.3expr 4.4bc(小数运算) 4.5$[ ] 4.6awk 4.7总结运算方式 五.条件测试语句 5.1文件 5.2条件测试表达式…

IDEA:git 回滚本地提交-git 选择 Reset Current Branch to

前言 回滚提交到本地但是还没有 Push 上去的提交 选择我们要回滚的节点&#xff0c;然后点击 git 选择 Reset Current Branch to… 再选择 Hard 。当我们点击 Reset 的时候&#xff0c;代码就会回滚到单前选中的这个版本

数字时代的工作利器

当谈到使用工作软件的多样选择时&#xff0c;就像是探索灯塔下的海洋般令人兴奋。无论是新进入办公领域的小白&#xff0c;还是经验丰富的职场老将&#xff0c;我们都渴望找到那些能在工作中为我们点燃生产力和创造力的魔法工具。下面是五款备受欢迎且富有创造力的工作软件推荐…

专业120+总分400+宁波大学912信号与系统考研经验电子信息通信集成电路光电

今年考研顺利上岸&#xff0c;专业课912信号与系统120&#xff0c;总分400&#xff0c;被宁波大学录取&#xff0c;回望这一年的复习有过迷茫和犹豫&#xff0c;也有过坚持和坚强&#xff0c;总结一下自己的复习得失&#xff0c;希望对大家复习有所帮助。专业课&#xff1a; 前…

【重磅发布】已开放!模型师入驻、转格式再升级、3D展示框架全新玩法…

1月23日&#xff0c;老子云正式发布全新版本。此次新版本包含多板块功能上线和升级&#xff0c;为用户带来了含模型师入驻、三维格式在线转换升级、模型免费增值权益开放、全新3D展示框架等一系列精彩内容&#xff01; 1月23日&#xff0c;老子云正式发布全新版本。此次新版本…

【vue】图片加载骨架

一、前言 在网速较低或者网站的服务器宽带只有几MB的情况下&#xff0c;网页中的图片加载时&#xff0c;要么空白&#xff0c;要么像打印机一样一行一行地“扫描”出来&#xff0c;为了提升用户体验&#xff0c;可以给图片标签外加一层骨架。 无骨架 有骨架 二、详细设计 每张…

现在我有三个代码块,分别都调用了同一个接口使用相同的数据,请问怎么精简代码,让他只调用一次接口,将数据存储起来让其他函数共同使用.

问题描述: 现在我有三个代码块: 一: const getData async () > {console.log(触发了getData接口)let resultData await getActivityInfo(activityId);console.log(resultData,resultData)let id resultData.id;let shareImg resultData.shareImglet shareSubtitle res…

JWT(JSON Web Token)详解以及在go-zero中配置的方法

目的 对用户进行身份认证和信息交换 RFC 7519 传统方式 通过session保存对话信息&#xff0c;服务端返回一个session id&#xff0c;用户保存这个id在cookie内&#xff0c;然后每次请求都传给服务端 局限性 对于服务器集群难以向每个服务器共享同一session jwt的方式是…

备战蓝桥杯---数据结构与STL应用(基础实战篇1)

话不多说&#xff0c;直接上题&#xff1a; 当然我们可以用队列&#xff0c;但是其插入复杂度为N,总的复杂度为n^2,肯定会超时&#xff0c;于是我们可以用链表来写&#xff0c;同时把其存在数组中&#xff0c;这样节点的访问复杂度也为o(1).下面是AC代码&#xff1a; 下面我们来…

学习MySQL仅此一篇就够了(视图)

视图 介绍及基本语法 视图&#xff08;View&#xff09;是一种虚拟存在的表。视图中的数据并不在数据库中实际存在&#xff0c;行和列数据来自定义视 图的查询中使用的表&#xff0c;并且是在使用视图时动态生成的。 通俗的讲&#xff0c;视图只保存了查询的SQL逻辑&#xf…

我在代码随想录|写代码Day20之二叉树-700. 二叉搜索树中的搜索,98. 验证二叉搜索树,530.二叉搜索树的最小绝对差

学习目标&#xff1a; 博主介绍: 27dCnc 专题 : 数据结构帮助小白快速入门 &#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d; ☆*: .&#xff61;. o(≧▽≦)…

TypeScript(八) number和string

1. TypeScript number 1.1. 描述 Number对象是原始数值的包装对象。 1.2.语法 var num new Number(value);;注意&#xff1a;如果一个参数值不能转换为一个数字&#xff0c;将返回NaN&#xff08;非数字值&#xff09;。 1.3. 对象属性 属性描述MAX_VALUE可表示的最大的数…

深度学习-搭建Colab环境

Google Colab(Colaboratory) 是一个免费的云端环境&#xff0c;旨在帮助开发者和研究人员轻松进行机器学习和数据科学工作。它提供了许多优势&#xff0c;使得编写、执行和共享代码变得更加简单和高效。Colab 在云端提供了预配置的环境&#xff0c;可以直接开始编写代码&#x…

【CMU-自主导航与规划】Autonomous Exploration Development Environment 配置与运行

官方文档&#xff1a;https://www.cmu-exploration.com/ 一、安装与配置&#xff1a; 适用版本&#xff1a; Ubuntu 18.04 ROS Melodic and Ubuntu 20.04 with ROS Noetic. 如果使用 Ubuntu 22.04 with ROS2 Humble, 则需要配置ROS2【https://drive.google.com/file/d/1a01RS…

鸿蒙新风口,湖南码牛助你高薪起飞!

去年9月底&#xff0c;在华为秋季全场景新品发布会上&#xff0c;华为常务董事、终端BG CEO余承东宣布&#xff0c;鸿蒙原生应用全面启动&#xff0c;HarmonyOS NEXT开发者预览版将在2024年第一季度开放。 HarmonyOS 2019年正式面世&#xff0c;至今已成长了4年&#xff0c;截至…

86.网游逆向分析与插件开发-物品使用-物品丢弃的逆向分析与C++代码的封装

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;物品使用的逆向分析与C代码的封装-CSDN博客 码云地址&#xff08;ui显示角色数据 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;7563f86877c…