搜狗面试剖析

 1.为什么基类的析构函数是虚函数?
答:编译器总是根据类型来调用类成员函数。但是一个派生类的指针可以安全地转化为一个基类的指针。这样删除一个基类的指针的时候,C++不管这个指针指向一个基类对象还是一个派生类的对象,调用的都是基类的析构函数而不是派生类的。如果你依赖于派生类的析构函数的代码来释放资源,而没有重载析构函数,那么会有资源泄漏。所以建议的方式是将析构函数声明为虚函数。如果你使用MFC,并且以CObject或其派生类为基类,那么MFC已经为你做了这件事情;CObject的析构函数是虚函数。一个函数一旦声明为虚函数,那么不管你是否加上virtual 修饰符,它在所有派生类中都成为虚函数。但是由于理解明确起见,建议的方式还是加上virtual 修饰符。
 2.Union和sturct区别(是以选择题形式出现,一个具体的例子)
答:1. struct和union都是由多个不同的数据类型成员组成, 但在任何同一时刻, union中只存放了一个被选中的成员, 而struct的所有成员都存在。在struct中,各成员都占有自己的内存空间,它们是同时存在的。一个struct变量的总长度等于所有成员长度之和。在Union中,所有成员不能同时占用它的内存空间,它们不能同时存在。Union变量的长度等于最长的成员的长度。 2. 对于union的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于struct的不同成员赋值是互不影响的。

 3.单链表逆置(c编程)

class Node
{
public:
	int value;
	Node* next;
};
Node* ReserveLink(Node* head)
{
	if(head==0 || head->next==0) return head;

	Node* p, *q, *temp;

	p= head->next;
	q=p->next;
	while(q)
	{
		temp = q->next;
		q->next = p;
		q = temp;
	}
	return p;
}

 

4.1~20的整数的全排列(c编程)
这个题暂时用C#来实现,如果各位有好的办法或者更高效的实现请跟帖讨论

public static string[] PrintArrangementVer1(string inItems)
{
	List list = new List();
	if (string.IsNullOrEmpty(inItems)) return null;
	else if (inItems.Length == 1)
	{
		list.Add(inItems.ToString());
	}
	else if (inItems.Length == 2)
	{
		list.Add(inItems);
		list.Add(inItems[1].ToString() + inItems[0].ToString());
	}
	else
	{
		for (int i = 0; i < inItems.Length; i++)
		{
			string[] strs = PrintArrangementVer1(inItems.Substring(0, i) +
			inItems.Substring(i + 1, inItems.Length - i - 1));
			foreach (string temp in strs)
			{
				list.Add(inItems[i].ToString() + temp);
			}
		}
	}
	return list.ToArray();
}

5.字符串拷贝(特殊要求是源地址和目标地址的空间有重叠的,C编程)

char * strcpy(char * strDest,const char * strSrc)
{
	if ((strDest==NULL)||(strSrc==NULL)) //[1]
		throw "Invalid argument(s)"; //[2]
	char * strDestCopy=strDest;  //[3]
	while ((*strDest++=*strSrc++)!=''); //[4]
	return strDestCopy;
}

/*
错误的做法:
    [1]
    (A)不检查指针的有效性,说明答题者不注重代码的健壮性。
    (B)检查指针的有效性时使用((!strDest)||(!strSrc))或(!(strDest&&strSrc)),说明答题者对C语言中
类型的隐式转换没有深刻认识。在本例中char *转换为bool即是类型隐式转换,这种功能虽然灵活,但更多
的是导致出错概率增大和维护成本升高。所以C++专门增加了bool、true、false三个关键字以提供更安全的
条件表达式。
    (C)检查指针的有效性时使用((strDest==0)||(strSrc==0)),说明答题者不知道使用常量的好处。直接
使用字面常量(如本例中的0)会减少程序的可维护性。0虽然简单,但程序中可能出现很多处对指针的检查,
万一出现笔误,编译器不能发现,生成的程序内含逻辑错误,很难排除。而使用NULL代替0,如果出现拼写错
误,编译器就会检查出来。
    [2]
    (A)return new string("Invalid argument(s)");,说明答题者根本不知道返回值的用途,并且他对内
存泄漏也没有警惕心。从函数中返回函数体内分配的内存是十分危险的做法,他把释放内存的义务抛给不知
情的调用者,绝大多数情况下,调用者不会释放内存,这导致内存泄漏。
    (B)return 0;,说明答题者没有掌握异常机制。调用者有可能忘记检查返回值,调用者还可能无法检查
返回值(见后面的链式表达式)。妄想让返回值肩负返回正确值和异常值的双重功能,其结果往往是两种功
能都失效。应该以抛出异常来代替返回值,这样可以减轻调用者的负担、使错误不会被忽略、增强程序的可
维护性。
    [3]
    (A)忘记保存原始的strDest值,说明答题者逻辑思维不严密。
    [4]
    (A)循环写成while (*strDest++=*strSrc++);,同[1](B)。
    (B)循环写成while (*strSrc!='') *strDest++=*strSrc++;,说明答题者对边界条件的检查不力。循
环体结束后,strDest字符串的末尾没有正确地加上''。
*/
6.关于http功能和tcp报头的
答:TCP协议头最少20个字节,包括以下的区域
TCP源端口(Source Port):16位的源端口其中包含初始化通信的端口。源端口和源IP地址的作用是标示报问的返回地址。
TCP目的端口(Destination port):16位的目的端口域定义传输的目的。这个端口指明报文接收计算机上的应用程序地址接口。
TCP序列号(序列码,Sequence Number):32位
TCP应答号(Acknowledgment Number):32位的序列号由接收端计算机使用,重组分段的报文成最初形式。如果设置了ACK控制位,这个值表示一个准备接收的包的序列码。
数据偏移量(HLEN):4位包括TCP头大小,指示何处数据开始。
保留(Reserved):6位值域,这些位必须是0。为了将来定义新的用途所保留。 标志(Code Bits):6位标志域。表示为:紧急标志、有意义的应答标志、推、重置连接标志、同步序列号标志、完成发送数据标志。按照顺序排列是:URG、ACK、PSH、RST、SYN、FIN。

7.sql语句

8.比较次数与初始序列无关的排序方法有哪些?

2 条关于 “搜狗面试剖析” 的评论

  1. 单链表逆置好像有问题。比较简洁的一个写法:

    void  ListInverse_L(LinkList &L){
         LNode *p,*q;
         p=L->next;
         L->next=NULL;
         while(p!=NULL){
             q=p->next;
             p->next=L->next;
             L->next=p;
             p=q;
         }
       }  
    

发表评论