C专家编程上的一些习题

本文探讨了如何在不可标记的链表中检测循环,包括多种限制条件下的解决方案,并介绍了系统调用与库函数调用的区别,强调了上下文切换和效率问题。还涉及如何使用宏判断变量是无符号数还是有符号数。
摘要由CSDN通过智能技术生成

第一题: 关于链表问题;

问题:怎样才能检测到链表中是否存在循环?

 

对访问过的每个元素做个标记,继续遍历这个链表,如果遇到某个已经做过标记的元素,说明链表中存在循环。

(如何做标记呢?思考之如果链表中的元素全部都是正整数的话,我们可以这样做,对访问过的每个元素加上个负号,然后我们继续遍历链表,如果访问到负数了,就说明链表里存在循环。如果既有正数又有负数呢?那么我们可以先遍历链表以此找出其中的最大值,然后重新遍历链表,并改变遍历过的元素值,使之大于最大值。然后继续遍历并和最大值进行比较。如果我们遍历到一个值大于最大值了,那么就证明链表中存在循环。如果链表中不是数字,是字母或是字符串呢? 对此我们可以同样也可以做些标志,如在我们访问过的字符串后面加上一个数字或是字符等.)

 

进一步限制: 这个链表位于只读内存区域,无法在元素上做标记.

 

当访问每个元素时,把它存储在一个数组中。检查每一个后继元素,看看它是否已经存在数组中。

(汗,竟然这样卡人,我认为应该首先遍历一遍链表看看链表中是否存在相同的元素值。若不存在我们可以使用上面的方法,若存在我们可以首先保存住相同元素的小标。然后再进行判断。)

 

再进一步限制: 噢,内存空间有限,无法创建一个足够长度的数组,然而,可以假定如果链表中存在循环,它出现在前n个元素之中.

 

设置一个指针指向链表的头部,然后依次比较该值和后n-1个元素,若存在相同的则说明有循环,否则,指针后移,比较它后面的n-i个元素。

(噢,这样的方法确实节省内存空间,仅使用了一个指针的内存空间。不过可能需要多次遍历链表。不过这也是没办法了)

 

继续限制:噢!不!链表的长度是任意的,而且循环可能出现在任意的位置。

 

首先排除一种特殊情况,就是3个元素的链表中第2个元素的后面是第1个元素。设置两个指针p1p2p1指向第一个元素,p2指向第三个元素,看看他们是否相等,如果相等就属于上述这种情况,如果不等,把p1后移一个元素,p2向后移两个元素。检查两个指针的值,如果相等,说明链表中存在循环,如果不等,继续按照前述方法进行。如果出现两个指针都为NULL,说明链表中不存在循环。如果链表中存在循环,用这种方法肯定能够检测出来,因为其中一个指针一定能够追上另一个。

(这种方法的关键点就在,p1后移一个元素,p2后移两个元素,这样我们让p2p1,如果存在循环,p2肯定能够追上p1.

 

2 系统调用和库函数调用

 

通过这个问题,可以判断候选人是否具有丰富的编程经验以及是否具有找出这类问题答案的敏锐感觉。

 

简明的回答是:函数库调用是语言或应用程序的一部分,而系统调用是操作系统的一部分。你要确保弄懂“trap(自陷)”这个关键字的含义。系统调用是在操作系统内核发现一个“trap”或中断后进行的。

 

※函数库调用 VS 系统调用

函数库调用 系统调用

在所有的ANSI C编译器版本中,C库函数是相同的 各个操作系统的系统调用是不同的

它调用函数库中的一段程序(或函数) 它调用系统内核的服务

与用户程序相联系 是操作系统的一个入口点

在用户地址空间执行 在内核地址空间执行

它的运行时间属于“用户时间” 它的运行时间属于“系统”时间

属于过程调用,调用开销较小 需要在用户空间和内核上下文环境间切换,开销较大

C函数库libc中有大约300个函数 UNIX中大约有90个系统调用

典型的C函数库调用:system fprintf malloc 典型的系统调用:chdir fork write brk

 

 

库函数调用通常比行内展开的代码慢,因为它需要付出函数调用的开销。但系统调用比库函数调用还要慢很多,因为它需要把上下文环境切换到内核模式。

 

其他解答:

库函数一般完成常见的特定功能,一般由某个组织制作发布,并形成一定的标准,库函
数一般可以应用于不同的平台而不需要做任何修改。例如,C 函数库能够被绝大多数 C 编译
器支持。
系统调用函数一般与操作系统相关,不同的操作系统所使用的系统调用可能不太一样。

一般来说,如果两个操作系统差异很大,系统调用函数的可移植性就不高。例如 Windows
用了系统调用的应用程序不能直接在 Linux 下编译运行。系统调用函数很多情况下需要访问
系统特殊资源,使用系统调用时,该程序的状态将从用户态切换到内核态。图 1-1 所示为 Linux
函数库调用和系统调用示意图。

1-1 库函数调用和系统调用示意图
库函数在实现中也有可能需要使用系统调用,但它封装了系统调用部分的操作。用户不

必关心它使用了哪些系统调用。另外,进行上层应用程序开发时也没有必要深入研究系统调
用函数的具体实现过程。

 

3 编写一些代码,确定一个变量是有符号数还是无符号数

显然这个问题我们无法使用函数来进行实现,因为函数我们都是直接定义好了的变量,他们的类型都是已知了的。故我们选择使用宏来实现这个。

我们都明白,一个无符号数在任何时候都不会是负数的,但是它的补码是负数。所以我们可以使用这样的宏来进行判断:

#define ISUNSIGNED(a) (a >= 0 && ~a >= 0)

 

如果是类型的话,我们可使用下面你的宏:

#define ISUNSIGNED(type) ((type)0 - 1 > 0)

这个宏的意思是,将0转换为相应的类型,如果是无符号数,-1之后就会得到最大的那个数值,负责将会得到-1.从而我们就可以判断所给的类型到底是无符号的还是有符号的了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值