Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1242057
  • 博文数量: 510
  • 博客积分: 20296
  • 博客等级: 上将
  • 技术积分: 4680
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-30 03:58
文章存档

2011年(13)

2010年(92)

2009年(242)

2008年(163)

我的朋友

分类: C/C++

2009-01-31 03:24:36

1)怎样才能检测到链表中存在循环
面试者可能如下作答

1.
   对访问过的每个元素做个标记,继续遍历这个链表,如果遇到某个已经做过标记的元素,说明链表存在循环。
        链表位于只读区域,无法在元素上做标记

2.
    当访问每个元素时,把它存在一个数组里。检查每个后据元素,看看它是否已经存在 数组中。(哈哈,也许有些人继续想用散列表来优化数组的访问)
        内存空间有限,无法创建一个足够长的数组。但是,可以假定如果链表中存在循环,那么它出现在前面的N个元素中

3.

设置一个指针,指向链表的头部。在接下去 对直到第N个元素的访问中,把N-1个元素依次同指向的元素进行比较。然后指针移向下一个元素,把同后面的N-2个元素进行比较。根据这个方法依次进行比较,如果出现比较相等的情况,就说明前N个元素中存在循环,否则如果所有N个元素两两之间进行比较都不相等,说明链表中不存在循环。

        链表的长度是任意的,而且循环可能出现在任何位置。

4.参考答案

 首先,排除一种情况:3个元素的链表 ,第2个元素的后面是第一个元素。
          设置两个指针P1和P2,P1指向一个元素,P2指向第3个元素,看看它们是否相等。如果相等就属于上述这种特殊情况。

    如果不等,把P1后移一个元素,P2后移两个元素。检查两个指针的值,如果相等,说明链表中存在循环。如果不相等,继续按照前述方法进行。如果出现两个指针都是NULL的情况,说明链表中不存在循环。

   如果链表中存在循环,用这种方法一定能检查出来,因为其中一个指针肯定能追上另一个(两个指针具有相同的值),尽管可能要对这个链表经过几次遍历才能检测出来。



(2)不同的增值语句有什么区别
x = x + 1;
++ x;
x ++;
x += 1;


需要提供适当的上下文才好找出其中的区别
(1)
x += 1; 是在算法语言中表达 "x = x + 1;"的便捷方法

(2)
++x 它先增加 x的值,然后再在周围的表达式中 使用x的值
x++ 先在周围的表达式中 使用x的值,然后再增加x的值

(3)
当x不是一个简单的变量而是一个涉及数组的表达式,像x += 1 这样的形式是很有用的。

如果你有一个复杂的数组引用,并需要证明 同一种 下标形式在两种引用中都可以使用,那么

node[i>>3] += -(0x01 << ( i & 0x7) );


就是你应该采用的方法。

(4)
左值(通常具有一个地址,它也可能是一个寄存器,也可能是地址或者寄存器加上一个位段)
只被计算了一次。
这意味着,表达式

mango[j++] += y;

被当作

mango[j] = mango[j] + y;
j++;

而不是

mango[j++] = mango[j++] + y;


有人解释说,这些区别与编译器的中间代码有关。例如,"++x"表示取地址x的地址,增加它的内容,然后把值放在寄存器中:"x++"则表示取x的地址,把它的值转入寄存器,然后增加在内存中的x的值

(5)
K & R 认为 ++ 比 直接加 1 更有效率。
但是 当代编译器 在没有区别的上下文中,产生的代码相同的指令,它们应该是 增加一个变量最快的一种指令。

一般较短的形式 比 较长的形式 更容易阅读一些。

然而,过度简洁的代码 也会导致代码难以阅读

frotz[--j + i++] += --y;
改成
--y;
--j;
frotz[j+i] = frotz[j+i] + y;
i++;
所以,不要在一行代码里实现太多的功能
因为这并不会使编译器 产生更有效率的代码,但会使你丧失调试代码的机会。

K & R 说:

人人都知道调试比第一次编写代码要难上一倍。所以,如果在编写代码时就把自己的聪明发挥到极致,那么调试时又该怎么办呢?


(3)库函数调用和系统调用简单的回答是
   库函数调用 是 语言或应用程序的一部分,
而 系统调用是操作系统的一部分。

你要确保 弄懂 trap的含义。系统调用是 操作系统内核 发现一个 trap 或者中断之后进行的。

库函数调用                                                                      系统调用

在ANSI C编译器中,C函数库是系统的                各个操作系统的系统调用是不同的。
                                                                                   符合Posix标准的OS,它们的系统调用是相同的吗?

调用函数库的一个程序                                      调用系统内核服务

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

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

运行时间属于“user”时间                                    属于“system"时间

属于过程调用,开销较小                             需要切换到内核上下文环境然后在
                                                                 切换回来,开销较大

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


典型调用
  system          fprintf    malloc    ----------            chdir fork write brk

但是,你必须记住:

  许多C函数库中的程序是 通过系统调用来实现功能的。
比如文件系统相关的操作
windows中fopen   大概就是调用CreateFile


(4)文件描述符和文件指针系统IO调用有 creat(),open(),read(),write(),close(),ioctl(),接受一个文件描述符,是一个整数,用于索引开放文件的每个进程表(per-process table -of -open -file)

为了确保程序的可移植性应该使用标准IO库调用,如fopen(0,fclose(),fputc(),fseek()等,它们绝大多数中的名字中带有一个"f"。这些调用都接受一个类型为FILE结构的指针(有时称为流指针)的参数。FILE指针指向一个流结构,在中定义。结构的内容根据编译器的不同而有所不同,在UNIX中通常是Open File的每个进程表的一个条目。在典型情况下,它包含了流缓冲区、 所有用于提示缓冲区有多少字节是实际的文件数据的变量以及提示流状态的标志(如ERROR和EOF)等

所以,文件描述符 就是 Open File中的每个进程表的一个偏移量(如"3")。它用于UNIX系统中,用于标识文件。

FILE指针保存了一个FILE结构的地址。FILE结构用于表示 开放的I/O流(如hex 20938).它用于ANSI C标准的IO库调用中,用于标识文件。


(5)确定一个变量unsigned 还是signed函数的参数形式是在函数内部定义的,所以你无法用函数来实现这个目的。

那么用 宏。

有符号数的本质就是 对最左边的一个位取补将会改变它的符号。由于其他位与这个测试无关,所以你可以将它的所有位都取补。

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


ANSI C的类型提升规则(所有的表达式中,如果是个变量,因为编译器无法判断结果是否溢出,都对类型进行提升)

char 
short int
bit(unsigned /signed)
enum
如果int能够完整的容纳原先的数据,否则将被转换为 unsigned int




gcc 居然丢出这样一句话来
warning: comparison is always true due to limited range of data type

如果是一个类型
#define ISUNSIGNED(type) ((type)0 - 1) > 0)
阅读(713) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~