C的温故而知新:The Last(C Primer Plus第十六、十七章)

The Last

想必是后面的内容超出大一课程规定的内容了,后面的文章数据惨的可怜。搜一下就能看见,好多人主张这本书后面几张可以不看。咋讲呢,对于这个观点来说,我本人是不认同的,我呢,粗略的看了看后两章的内容,可能对于初期学习确实作用不大,但是如果想有所提升的话,这两章的内容还是蛮重要的。第十六章讲述了C语言的预处理器和常用库文件。第十七章更是为我们介绍了高级的数据结构,不过毕竟数据结构后面还是会学的。因为现在已经九月份了,时间过得真的很快,我要开始下一步安排了,我们快速地将两章的内容过一遍。对于这两章的内容来讲,不仔细的看完是很可惜的,后面有时间我还会再看的,毕竟书这东西,常读常新嘛。

C预处理器和C库

明示常量,用#define指令来定义,从指令出现的位置到文件结尾都有效,需要注意的是,预处理器不会进行实际的计算,只进行替换。在#define中还可以使用参数,可以实现类函数宏。#运算符,可以将参数字符串化。##运算符称为预处理器黏合剂,可用于类函数宏或对象宏的替换部分。

文件包含,#include,一般来说,文件名在尖括号之中的话,代表标准文件,在标准系统目录中查找该文件。文件名在双引号之中的话,代表自定义文件,一般从当前目录开始查找该文件。

#undef,用于撤销已定义的#define指令。

#ifdef、#else和#endif,如果已经存在#define定义,执行相应指令,如果不存在该定义,执行其余指令,结束分支判断。

#ifndef,和上面类似,只是意思相反。

#if、#elif和#endif,分支判断,if后面一般是真或假的表达式。

预定义宏

在这里插入图片描述

#line,重置_ _LINE_ _和_ _FILE _ _宏报告的行号和文件名。

#error,让预处理器发出一条错误信息,如果可能的话,编译过程应该中断。

#pragma,把编译器指令放入源代码中。

泛型选择,如果x为某种类型,则赋给其对应的值,如果都不满足,赋值default对应的值。

_Generic(x,int: 0, float: 1, double: 2, default: 3)

内联函数,用说明符inline static修饰的函数,省去了函数原型,将函数定义和函数原型合二为一。

_Noreturn函数,函数说明符_Noreturn修饰的函数,表示调用完成后不返回主调函数,比如exit()函数。

C库:

我的理解就是各类标准函数,访问方式有三种,自动访问、文件包含以及库包含。常用的就是引用文件。

math.h
在这里插入图片描述

tgmath.h:可以理解为,为了处理更多类型的数据,将math.h中的函数参数都添加了泛型选择,去执行不同的内容,得到不同的结果。

通用工具库,函数原型一般在stdlib.h头文件中,比如随机数生成函数、查找和排序函数、转换函数和内存管理函数

exit()函数:刷新所有输出流、关闭所有打开的流和关闭由标准I/O函数tmpfile()创建的临时文件。终止程序。

atexit()函数:接受函数指针作为参数,退出时调用特定函数。

qsort()函数:数组快速排序的函数,接受四个参数,第一个为数组首元素指针,第二个为数组元素个数,第三个为数组元素类型大小,第四个为比较函数的指针。比较函数要自己根据数组元素类型以及升降序,视情况进行编写。

断言库:assert.h头文件,常用于调试程序,assert()函数内的表达式如果为假,会报告错误以及出问题的位置,还可以通过#define NDEBUG来动态的对该语句进行禁用。_Static_assert()会在编译的时候就结束,第一个参数为整型常量表达式,第二个为字符串,当第一个参数值为0时,显示字符串且终止编译。

string.h中的memcpy()和memmove()函数,将数组元素复制到另一个数组,接受三个参数,将第二个参数的数组内容复制到第一个参数的数组之中,第三个参数表示复制的字节数。

可变参数:stdarg.h,函数至少有一个参数和一个省略号,省略号在最后,之前的最后一个形参用parmN来描述,实际上是代表省略号部分的参数数量。va_start()把参数列表拷贝到va_list类型的变量中,两个参数,一个va_list类型的变量,一个parmN形参。va_arg()访问参数列表,两个参数为va_list类型的变量和一个类型名,循环依次调用。最后使用va_end()来清理,接受一个va_list类型的参数。va_copy(),将第二个参数的va_list变量拷贝给第一个参数的变量。

高级数据表示

链表

首先就是链表,创建链表,显示链表,以及释放链表。定义一个结构,存储数据以及下一个结构的指针。定义三个指针,分别表示头,也就是第一个结构的位置,当前的结构位置以及上一个结构的位置,也就是当前的尾。使用malloc()函数创建合适的空间,然后将返回的地址放到,如果为第一个,则放到头指针中,如果不是,放到当前的结构位置,并且将当前的尾,也就是上一个指针的next从空变为指向当前位置,将当前的next指向设置为空。依次进行接下的操作。显示,从头指针开始,循环展示。注意一定要时刻保证头指针的位置,别改完之后找不到了。释放链表,从头指针开始,依次用free()释放,并将头指针移动到下一个结构。删除链表中单个数据的话,就是释放指定位置的结构空间,在此之前,将删除的位置的前后再链接起来即可。

抽象数据类型(ADT)

把一类事物整体来看,将它的属性和操作整理一下,放到一个文件之中,直接拿过来使用,不用关心具体是如何实现的操作。在C语言的技术角度来看,没有接口文件嘛,将数据以及函数原型放到头文件中,然后用一个代码文件实现头文件中的接口,然后再目标文件中引入接口文件,使用对应的内容。三个文件一起编译生成一个可执行文件。

队列

具有特殊属性的链表,新项只能添加到链表的末尾,只能在链表的开头移除项。先进先出,好比排队买东西,理想情况下没有无赖插队,都自觉的排到队伍末尾,然后买完的离开队伍,下一位接上。

链表和数组

在这里插入图片描述

因为链表不支持随机访问,所以不能使用二分查找。综上,要根据具体的问题选择数据类型,如果需要频繁地插入或删除项的话,而且不经常查找,选择链表更好。如果只是偶尔插入或移除项,但是经常进行查找的话,数组更好。

二叉查找树

如果既需要频繁插入和删除项,有需要频繁地查找数据,就要使用二叉查找树了。一种特殊的链表,结合二分查找算法的链接结构,每一个节点都包含两个指向其他子节点,左节点和右节点。左侧的子节点在父节点的前面,右侧的子节点在父节点的后面,从根节点开始,每次比较都可以排除一个子树。二叉查找树只有在满员平衡的时候效率最高。如果只依次增加左子树或者右子树,效率就不如链表了。

二叉树的实现思路:添加项,检查树中是否存在该项,空间是否满足,然后找到合适的位置,链接。查找项,从根节点开始向下,递归查左子树和右子树。删除项,三种情况,删除叶子结点,直接将叶子结点的父节点的链接指针设置为空,将空间释放即可。第二种,带有一个子节点的项,将子节点与它原本与父节点相连的位置相连,将该节点内存清空。第三种情况,将一个带有两个子节点的节点删除,比较麻烦,首先删除它之后,会有两个子树,这很明显无法连接到父节点的空缺的位置上,看这个要删除的节点在父节点属于左节点还是右节点,我们按照左节点来介绍,将该节点的左子树连接到父节点的左节点位置,然后查找左子树的右节点,依次往下,直到找到一个右节点为空的位置,然后将右子树连接到该位置。遍历树,循环遍历左子树和右子树,使用递归比较好,清空树,遍历,释放内存位置即可。

小结

以上就是本书最后的一部分内容了,这两章的编程练习我不打算一个个的实现了,因为看了一下,也不是很难,要求并不复杂,只是要我们运用这两章所学的知识,只是我现在还要去看一些其他的算法编程题,会用到这些东西的,就没必要在这里做这些了,毕竟要做这些也要几天的时间。所以这也是本章题目叫做The Last的原因。作为C Primer Plus学习笔记的最后一篇记录,但这绝不是结束,因为日后还有很长的一段路要走,这本书也一定会常常回顾的。好,闲言少叙,回见了,各位!!!

  • 15
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值