or, in code,
int h = 0; for (int i = 0; i < n; i++) { h = 31*h + s.charAt(i); }
In general the arithmetic operations in such expressions will use 32-bit modular arithmetic ignoring overflow
在实际中,BKDRhash函数比较好
// BKDR Hash unsigned int BKDRHash(char *str) { unsigned int seed = 131; // 31 131 1313 13131 131313 etc.. unsignedint hash = 0; while (*str) { hash = hash * seed + (*str++); } return (hash & 0x7FFFFFFF); }
6.tcp三次握手的过程,accept发生在三次握手哪个阶段?
三次握手:C----->SYN K
S------>ACK K+1 SYN J
C------->ACK J+1
DONE!
client 的 connect 引起3次握手
server 在socket, bind, listen后,阻塞在accept,三次握手完成后,accept返回一个fd,
因此accept发生在三次握手之后。。。。。。
7.Tcp流, udp的数据报,之间有什么区别,为什么TCP要叫做数据流?TCP本身是面向连接的协议,S和C之间要使用TCP,必须先建立连接,数据就在该连接上流动,可以是双向的,没有边界。所以叫数据流 ,占系统资源多
UDP不是面向连接的,不存在建立连接,释放连接,每个数据包都是独立的包,有边界,一般不会合并。
TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证8.const的含义及实现机制,比如:const int i,是怎么做到i只可读的?
const指示对象为常量,只读。
实现机制:这些在编译期间完成,对于内置类型,如int, 编译器可能使用常数直接替换掉对此变量的引用。而对于结构体不一定。
看下面的例子:
const int j=100; int *p=const_cast<int*>(&j); *p=200; cout<<j<<endl; 输出为什么是100呢?
cout<<*p<<endl; //输出是改过的200
编译器在优化代码时把cout<<j直接优化成cout<<100了,所以虽然p和&j的值一样,但cout<<j不再通过访问j的地址输出。(反汇编时也有看到直接把数字压栈push 100 )
这是因为,const型在压栈时,是使用的直接的数,就有点像C的#define a 100
对于非系统缺省类型,系统不知道怎么去直接替换,因此必须占据内存。
#include <iostream> using namespace std; struct A { int i; char ch; A() { i = 100; ch = 'S'; } }; int main() { const A a; const int i = 200; int *p1 = (int*)&a.i; int *p2 = (int*)&i; *p1 = 1; *p2 = 2; // a.i = 200; //报错,左值不能为const cout << a.i << " " << a.ch << endl; cout << i << endl; return 0; }
运行结果:
1 S 200
9.volatile的含义。
变量可能在编译器的控制或监控之外改变,告诉编译器不要优化该变量,如被系统时钟更新的变量。
10.OFFSETOF(s, m)的宏定义,s是结构类型,m是s的成员,求m在s中的偏移量。
#define OFFSETOF(s, m) size_t(&((s*)0)->m)
11.100亿个数,求最大的1万个数,并说出算法的时间复杂度。
用小根堆来实现。注意是小根堆,
读入1万个数,然后做
时间复杂度是O(NlogK)
12.设计一个洗牌的算法,并说出算法的时间复杂度。
第一种: for i:=1 to n do swap(a[i], a[random(1,n)]); // 凑合,但不是真正随机
第二种: for i:=1 to n do swap(a[i], a[random(i,n)]); // 真正的随机算法
其中,random(a,b)函数用于返回一个从a到b(包括a和b)的随机整数。
至于怎么证明上两个算法,没想好。
算法复杂度是O(n。。。),要研究下random的实现。
13.socket在什么情况下可读?
1. 接收缓冲区有数据,一定可读
2. 对方正常关闭socket,也是可读
3. 对于侦听socket,有新连接到达也可读
4.socket有错误发生,且pending~~~
引用unp的一段话 第六章 6.3节
A socket is ready for reading if any of the following four conditions is true:
a. The number of bytes of data in the socket receive buffer is greater than or
equal to the current size of the low-water mark for the socket receive buffer.
A read operation on the socket will not block and will return a value greater than 0
b. The read half of the connections is closed (i.e., A TCP connection that has received a FIN).
A read operation on the socket will not block and will return 0 (i.e., EOF)
c. The socket is a listening socket and the number of completed connection is nonzero.
An accept on the listening socket will normally not block, although we will describe a
d. A socket error is pending. A read operation on the socket will not block and will return
an error (-1) with errno set to the specific error condition
14.流量控制与拥塞控制的区别,节点计算机怎样感知网络拥塞了???
拥塞控制是把整体看成一个处理对象的,流量控制是对单个的节点。
感知的手段应该不少,比如在TCP协议里,TCP报文的重传本身就可以作为拥塞的依据。依据这样的原理, 应该可以设计出很多手段。
15.C++虚函数是如何实现的???
使用虚函数表。 C++对象使用虚表, 如果是基类的实例,对应位置存放的是基类的函数指针;如果是继承类,对应位置存放的是继承类的函数指针(如果在继承类有实现)。所以 ,当使用基类指针调用对象方法时,也会根据具体的实例,调用到继承类的方法。
16.C++的虚函数有什么作用? ??
虚函数作用是实现多态,
更重要的,虚函数其实是实现封装,使得使用者不需要关心实现的细节。
在很多设计模式中都是这样用法,例如Factory、Bridge、Strategy模式。
17. 非阻塞connect()如何实现? ??
将socket设置成non-blocking,操作方法同非阻塞read()、write();
18. 以下代码输出结果:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("call execl");
sleep(1);
execl("/bin/sh", "", NULL);
printf("error!\n");
}
本题考标准IO缓冲,标准出错是不带缓缓冲的。如若是涉及终端设备的其他流,则他们是行缓冲的;否则是全缓冲的。