常见的网络编程面试题

http://www.360doc.com/content/14/0724/21/1073512_396828405.shtml


1:tcp和udp的区别

TCP:是面向连接的流传输控制协议,具有高可靠性,确保传输数据的正确性,有验证重发机制,因此不会出现丢失或乱序。

UDP:是无连接的数据报服务,不对数据报进行检查与修改,无须等待对方的应答,会出现分组丢失、重复、乱序,但具有较好的实时性,UDP段结构比TCP的段结构简单,因此网络开销也小。

2:流量控制和拥塞控制

拥塞控制
网络拥塞现象是指到达通信子网中某一部分的分组数量过多,使得该部分网络来不及处理,以致引起这部分乃至整个网络性能下降的现象,严重时甚至会导致网络通信业务陷入停顿,即出现死锁现象。拥塞控制是处理网络拥塞现象的一种机制。
流量控制
数据的传送与接收过程当中很可能出现收方来不及接收的情况,这时就需要对发方进行控制,以免数据丢失。

3:多线程如何同步

windows

线程同步有四种方式:临界区、内核对象、互斥量、信号量

Linux

线程同步有最常用的是:互斥锁、条件变量和信号量。

4:进程间通讯的方式有哪些,各有什么优缺点

进程间通信

Linux 进程间通信(IPC)以下以几部分发展而来:
早期UNIX进程间通信、基于System V进程间通信、基于Socket进程间通信和POSIX进程间通信。
UNIX进程间通信方式包括:管道、FIFO、信号。
System V进程间通信方式包括:System V消息队列、System V信号灯、System V共享内存、
POSIX进程间通信包括:posix消息队列、posix信号灯、posix共享内存。
现在linux使用的进程间通信方式:
(1)管道(pipe)和有名管道(FIFO)
(2)信号(signal)
(3)消息队列
(4)共享内存
(5)信号量
(6)套接字(socket)

5:tcp连接建立的时候3次握手,断开连接的4次握手的具体过程

建立连接采用的3次握手协议,具体是指:
第一次握手是客户端connect连接到server,server accept client的请求之后,向client端发送一个消息,相当于说我都准备好了,你连接上我了,这是第二次握手,第3次握手就是client向server发送的,就是对第二次握手消息的确认。之后client和server就开始通讯了。
断开连接的4次握手,具体如下:
断开连接的一端发送close请求是第一次握手,另外一端接收到断开连接的请求之后需要对close进行确认,发送一个消息,这是第二次握手,发送了确认消息之后还要向对端发送close消息,要关闭对对端的连接,这是第3次握手,而在最初发送断开连接的一端接收到消息之后,进入到一个很重要的状态time_wait状态,这个状态也是面试官经常问道的问题,最后一次握手是最初发送断开连接的一端接收到消息之后。对消息的确认。

6:epoll与select的区别

select在一个进程中打开的最大fd是有限制的,由FD_SETSIZE设置,默认值是2048。不过 epoll则没有这个限制,它所支持的fd上限是最大可以打开文件的数目,这个数字一般远大于2048,一般来说内存越大,fd上限越大,1G内存都能达到大约10w左右。

select的轮询机制是系统会去查找每个fd是否数据已准备好,当fd很多的时候,效率当然就直线下降了,epoll采用基于事件的通知方式,一旦某个fd数据就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,而不需要不断的去轮询查找就绪的描述符,这就是epoll高效最本质的原因。

无论是select还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的,而select则做了不必要的拷贝

7:epoll中et和lt的区别与实现原理

LT:水平触发,效率会低于ET触发,尤其在大并发,大流量的情况下。但是LT对代码编写要求比较低,不容易出现问题。LT模式服务编写上的表现是:只要有数据没有被获取,内核就不断通知你,因此不用担心事件丢失的情况。
ET:边缘触发,效率非常高,在并发,大流量的情况下,会比LT少很多epoll的系统调用,因此效率高。但是对编程要求高,需要细致的处理每个请求,否则容易发生丢失事件的情况。


=======================================================================

1、connect方法会阻塞,请问有什么方法可以避免其长时间阻塞?
答:最通常的方法最有效的是加定时器;也可以采用非阻塞模式。

2、网络中,如果客户端突然掉线或者重启,服务器端怎么样才能立刻知道?
答:若客户端掉线或者重新启动,服务器端会收到复位信号,每一种tcp/ip得实现不一样,控制机制也不一样。

3.在子网210.27.48.21/30种有多少个可用地址?分别是什么?
答:

简:
30表示的是网络号(network number)是30位,剩下2位中11是广播(broadcast)地址,00是multicast地址,只有01和10可以作为host address。

详:
210.27.48.21/30代表的子网的网络号是30位,即网络号是210.27.48.21 & 255.255.255.251=210.27.48.20,此子网的地址空间是2位,即可以有4个地址:210.27.48.20, 210.27.48.21, 210.27.48.22, 210.27.48.23。第一个地址的主机号(host number/id)是0,而主机号0代表的是multicast地址。最后一个地址的最后两位是11,主机号每一位都为1代表的是广播(broadcast)地址。所以只有中间两个地址可以给host使用。其实那个问题本身不准确,广播或multicast地止也是可以使用的地址,所以回答4也应该正确,当然问的人也可能是想要你回答2。我个人觉得最好的回答是一个广播地址,一个multicast地址,2个unicast地址。

4.TTL是什么?有什么用处,通常那些工具会用到它?(ping? traceroute? ifconfig? netstat?)
答:
简:TTL是Time To Live,一般是hup count,每经过一个路由就会被减去一,如果它变成0,包会被丢掉。它的主要目的是防止包在有回路的网络上死转,浪费网络资源。ping和traceroute用到它。

详:TTL是Time To Live,目前是hup count,当包每经过一个路由器它就会被减去一,如果它变成0,路由器就会把包丢掉。IP网络往往带有环(loop),比如子网A和子网B有两个路由器相连,它就是一个loop。TTL的主要目的是防止包在有回路的网络上死转,因为包的TTL最终后变成0而使得此包从网上消失(此时往往路由器会送一个ICMP包回来,traceroute就是根据这个做的)。ping会送包出去,所以里面有它,但是ping不一定非要不可它。traceroute则是完全因为有它才能成的。ifconfig是用来配置网卡的,netstat -rn 是用来列路由表的,所以都用不着它

5.路由表示做什么用的?在linux环境中怎么来配置一条默认路由?
答:
简:路由表是用来决定如何将包从一个子网传送到另一个子网的,换局话说就是用来决定从一个网卡接收到的包应该送的哪一张网卡上的。在Linux上可以用“route add default gw <默认路由器IP>”来配置一条默认路由。

详:路由表是用来决定如何将包从一个子网传送到另一个子网的,换局话说就是用来决定从一个网卡接收到的包应该送的哪一张网卡上的。路由表的每一行至少有目标网络号、netmask、到这个子网应该使用的网卡。当路由器从一个网卡接收到一个包时,它扫描路由表的每一行,用里面的netmask和包里的目标IP地址做并逻辑运算(&)找出目标网络号,如果此网络号和这一行里的网络号相同就将这条路由保留下来做为备用路由,如果已经有备用路由了就在这两条路由里将网络号最长的留下来,另一条丢掉,如此接着扫描下一行直到结束。如果扫描结束任没有找到任何路由,就用默认路由。确定路由后,直接将包送到对应的网卡上去。在具体的实现中,路由表可能包含更多的信息为选路由算法的细节所用。题外话:路由算法其实效率很差,而且不scalable,解决办法是使用IP交换机,比如MPLS。
在Linux上可以用“route add default gw <默认路由器IP>”来配置一条默认路由。

6.在网络中有两台主机A和B,并通过路由器和其他交换设备连接起来,已经确认物理连接正确无误,怎么来测试这两台机器是否连通?如果不通,怎么来判断故障点?怎么排除故障?
答:测试这两台机器是否连通:从一台机器ping另一台机器
     如果ping不通,用traceroute可以确定是哪个路由器不能连通,然后再找问题是在交换设备/hup/cable等。

7.网络编程中设计并发服务器,使用多进程 与 多线程 ,请问有什么区别? 
答案一:
1,进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。
2,线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
两者都可以提高程序的并发度,提高程序运行效率和响应时间。
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

答案二:
根本区别就一点:用多进程每个进程有自己的地址空间(address space),线程则共享地址空间。所有其它区别都是由此而来的:
1。速度:线程产生的速度快,线程间的通讯快、切换快等,因为他们在同一个地址空间内。
2。资源利用率:线程的资源利用率比较好也是因为他们在同一个地址空间内。
3。同步问题:线程使用公共变量/内存时需要使用同步机制还是因为他们在同一个地址空间内。
等等


3. 网络编程的一般步骤

对于TCP连接:

1.服务器端1)创建套接字create;2)绑定端口号bind;3)监听连接listen;4)接受连接请求accept,并返回新的套接字;5)用新返回的套接字recv/send;6)关闭套接字。

2.客户端1)创建套接字create; 2)发起建立连接请求connect; 3)发送/接收数据send/recv;4)关闭套接字。

TCP总结:

Server端:create -- bind -- listen--  accept--  recv/send-- close

Client端:create------- conncet------send/recv------close.


对于UDP连接:

1.服务器端:1)创建套接字create;2)绑定端口号bind;3)接收/发送消息recvfrom/sendto;4)关闭套接字。

2.客户端:1)创建套接字create;2)发送/接收消息sendto/recvfrom;3)关闭套接字.

UDP总结:

Server端:create----bind ----recvfrom/sendto----close

Client端:create----  sendto/recvfrom----close.


5. TCP的重发机制是怎么实现的?

       1.滑动窗口机制,确立收发的边界,能让发送方知道已经发送了多少(已确认)、尚未确认的字节数、尚待发送的字节数;让接收方知道(已经确认收到的字节数)。

       2.选择重传,用于对传输出错的序列进行重传。

6. TCPUDP的区别?

       1)TCP面向连接(三次握手机制),通信前需要先建立连接;UDP面向无连接,通信前不需要建立连接;

       2)TCP保障可靠传输(按序、无差错、不丢失、不重复);UDP不保障可靠传输,使用最大努力交付;

       3)TCP面向字节流的传输,UDP面向数据报的传输。

   TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快


8.TCP为什么不是两次连接?而是三次握手?

如果AB两个进程通信,如果仅是两次连接。可能出现的一种情况就是:A发送完请报文以后,由于网络情况不好,出现了网络拥塞,即B延时很长时间后收到报文,即此时A将此报文认定为失效的报文。B收到报文后,会向A发起连接。此时两次握手完毕,B会认为已经建立了连接可以通信,B会一直等到A发送的连接请求,而A对失效的报文回复自然不会处理。依次会陷入B忙等的僵局,造成资源的浪费。

9. connect方法会阻塞,请问有什么方法可以避免其长时间阻塞?

可以考虑采用异步传输机制,同步传输与异步传输的主要区别在于同步传输中,如果调用recvfrom后会一致阻塞运行,从而导致调用线程暂停运行;异步传输机制则不然,会立即返回。


8.网络编程中设计并发服务器,使用多进程多线程,请问有什么区别?

答案一:

1,进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。

2,线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。两者都可以提高程序的并发度,提高程序运行效率和响应时间。

线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

答案二:

根本区别就一点:用多进程每个进程有自己的地址空间(address space),线程则共享地址空间。所有其它区别都是由此而来的:

1。速度:线程产生的速度快,线程间的通讯快、切换快等,因为他们在同一个地址空间内。

2。资源利用率:线程的资源利用率比较好也是因为他们在同一个地址空间内。

3。同步问题:线程使用公共变量/内存时需要使用同步机制还是因为他们在同一个地址空间内。

等等


17.流量控制和拥塞控制的实现机制

拥塞控制
网络拥塞现象是指到达通信子网中某一部分的分组数量过多,使得该部分网络来不及处理,以致引起这部分乃至整个网络性能下降的现象,严重时甚至会导致网络通信业务陷入停顿,即出现死锁现象。拥塞控制是处理网络拥塞现象的一种机制。
流量控制
数据的传送与接收过程当中很可能出现收方来不及接收的情况,这时就需要对发方进行控制,以免数据丢失。

流量控制机制:

  流量控制用于防止在端口阻塞的情况下丢帧,这种方法是当发送或接收缓冲区开始溢出时通过将阻塞信号发送回源地址实现的。流量控制可以有效的防止由于网络中瞬间的大量数据对网络带来的冲击,保证用户网络高效而稳定的运行。

18.多线程如何同步:

 在这里简单说一下linux多线程同步的方法吧(win上有一定的差别,也有一定的累似)

1:线程数据,每个线程数据创建一个键,它和这个键相关联,在各个线程里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个线程里,它代表同样的数据内容。以此来达到线程安全的目的。
2:互斥锁,就是在各个线程要使用的一些公共数据之前加锁,使用之后释放锁,这个是非常常用的线程安全控制的方法,而频繁的加解锁也对效率有一定的影响。
3:条件变量,而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步。
4:信号量,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。当公共资源增加时,调用函数sem_post()增加信号量。只有当信号量值大于0时,才能使用公共资源,使用后,函数sem_wait()减少信号量。函数sem_trywait()和函数pthread_ mutex_trylock()起同样的作用,它是函数sem_wait()的非阻塞版本
另外pthread_join也可以等待一个线程的终止。

19.进程间通讯的方式有哪些,各有什么优缺点

进程间通信主要包括管道, 系统IPC(包括消息队列,信号量,共享存储), socket.

管道包括三种:1)普通管道PIPE, 通常有种限制,一是半双工,只能单向传输;二是只能在父子进程间使用. 2)流管道s_pipe: 去除了第一种限制,可以双向传输. 3)命名管道:name_pipe, 去除了第二种限制,可以在许多并不相关的进程之间进行通讯.

系统IPC的三种方式类同,都是使用了内核里的标识符来识别

管道: 优点是所有的UNIX实现都支持, 并且在最后一个访问管道的进程终止后,管道就被完全删除;缺陷是管道只允许单向传输或者用于父子进程之间

系统IPC: 优点是功能强大,能在毫不相关进程之间进行通讯; 缺陷是关键字KEY_T使用了内核标识,占用了内核资源,而且只能被显式删除,而且不能使用SOCKET的一些机制,例如select,epoll等.

socket可以跨网络通讯,其他进程间通讯的方式都不可以,只能是本机进程通讯。


20.tcp连接建立的时候3次握手的具体过程,以及其中的每一步是为什么

建立连接采用的3次握手协议,具体是指:

第一次握手是客户端connect连接到server,server accept client的请求之后,向client端发送一个消息,相当于说我都准备好了,你连接上我了,这是第二次握手,第3次握手就是client向server发送的,就是对第二次握手消息的确认。之后client和server就开始通讯了。


21.tcp断开连接的具体过程,其中每一步是为什么那么做

断开连接的4次握手,具体如下:

断开连接的一端发送close请求是第一次握手,另外一端接收到断开连接的请求之后需要对close进行确认,发送一个消息,这是第二次握手,发送了确认消息之后还要向对端发送close消息,要关闭对对端的连接,这是第3次握手,而在最初发送断开连接的一端接收到消息之后,进入到一个很重要的状态time_wait状态,这个状态也是面试官经常问道的问题,最后一次握手是最初发送断开连接的一端接收到消息之后。对消息的确认。



=======================

1.C++模板的作用。 
将算法与具体对象分离,与类型无关,通用,节省精力

2.socket编程,如果client断电了,服务器如何快速知道???
有以下几个技术:
使用定时器(适合有数据流动的情况); 使用socket选项SO_KEEPALIVE(适合没有数据流动的情况); 
 

3.fork()一子进程程后 父进程癿全局变量能不能使用???
fork后子进程将会拥有父进程的几乎一切资源,父子进程的都各自有自己的全局变量。不能通用,不同于线程。对于线程,各个线程共享全局变量。

4.4G的long型整数中找到一个最大的,如何做????
我的想法是要找到最大的肯定要遍历所有的数的,而且不能将数据全部读入内存,可能不足。算法的时间复杂度肯定是O(n)
感觉就是遍历,比较。。。。还能怎么改进呢????
可以改进的地方,就是读入内存的时候,一次多读些。。。。
需 要注意的就是每次从磁盘上尽量多读一些数到内存区,然后处理完之后再读入一批。减少IO次数,自然能够提高效率。而对于类快速排序方法,稍微要麻烦一些: 分批读入,假设是M个数,然后从这M个数中选出n个最大的数缓存起来,直到所有的N个数都分批处理完之后,再将各批次缓存的n个数合并起来再进行一次类快 速排序得到最终的n个最大的数就可以了。在运行过程中,如果缓存数太多,可以不断地将多个缓存合并,保留这些缓存中最大的n个数即可。由于类快速排序的时 间复杂度是O(N),这样分批处理再合并的办法,依然有极大的可能会比堆和败者树更优。当然,在空间上会占用较多的内存。 

此题还有个变种,就是寻找K个最大或者最小的数。有以下几种算法:
容量为K的最大堆/最小堆,假设K可以装入内存;
如果N个数可以装入内存,且都小于MAX,那么可以开辟一个MAX大的数组,类似计数排序。。。从数组尾部扫描K个最大的数,头部扫描K个最小的数。
 

5.有千万个string在内存怎么高速查找,插入和删除???
对千万个string做hash,可以实现高速查找,找到了,插入和删除就很方便了。
关键是如何做hash,对string做hash,要减少碰撞频率。
In the String class, for example, the hash code h of a string s of length n is calculated as
\( \texttt{h} \;=\; \texttt{s[0]}*31^{n-1} + \texttt{s[1]}*31^{n-2} + \cdots + \texttt{s[n-1]} \)
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.. unsigned inthash = 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缓冲,标准出错是不带缓缓冲的。
如若是涉及终端设备的其他流,则他们是行缓冲的;否则是全缓冲的。

printf是标准IO的一个,格式化打印到标准输出,在这里是行缓冲,那么没有遇到换行符也就是‘\n’或者没有强制flush, 则不会输出。
execl是创建新的可执行程序映像,一旦成功就不会返回了,只有在出错的情况会返回1.
所以以上的程序没有打印printf的内容,直接执行/bin/sh,输出为
$

若是代码改为以下:
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    printf("call execl\n");
    /*fprintf(stderr, "%s", "call execl");*/
    sleep(1);
    execl("/bin/sh", "", NULL);
    printf("error!\n");

    return 0;
}
则输出为:
call execl
$

若改为:
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    /*printf("call execl\n");*/
    fprintf(stderr, "%s", "call execl");       // 标准错误,不缓冲
    sleep(1);
    execl("/bin/sh", "", NULL);
    printf("error!\n");

    return 0;
}
则输出为:
call execl$

若改为:
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    /*printf("call execl\n");*/
    fprintf(stdout, "%s", "call execl");     // stdout行缓冲
    sleep(1);
    execl("/bin/sh", "", NULL);
    printf("error!\n");

    return 0;
}
则输出为
$

19. TCP通讯中,select到读事件,但是读到的数据量是0,为什么,如何解决????
select 返回0代表超时。select出错返回-1。

select到读事件,但是读到的数据量为0,说明对方已经关闭了socket的读端。本端关闭读即可。

当select出错时,会将接口置为可读又可写。这时就要通过判断select的返回值为-1来区分。

20.  给出float与“零值”比较的 if 语句(假设变量名为var)???
const float EPSINON = 0.00001;
  if ((x >= - EPSINON) && (x <= EPSINON)

浮点数在内存中的存贮机制和整型数不同,有舍入误差在计算机中用以近似表示任意某个实数。具体的说,这个实数由一个整数或定点数(即尾数)乘以某个基数(计算机中通常是2)的整数次幂得到,这种表示方法类似于基数为10的科学记数法。
  所以浮点数在运算过成功运算通常伴随着因为无法精确表示而进行的近似或舍入。但是这种设计的好处是可以在固定的长度上存储更大范围的数。
  例如,一个指数范围为±4的4位十进制浮点数可以用来表示43210,4.321或0.0004321,但是没有足够的精度来表示432.123和43212.3(必须近似为432.1和43210)。当然,实际使用的位数通常远大于4。  
  所以浮点数不能够判断相等像 if(x==0)的这样的编码是不总是正确的,我们在判断浮点数相等时,推荐用范围来确定,若x在某一范围内,我们就认为相等,至于范围怎么定义,要看实际情况而已了,float,和double 各有不同
  所以const float EPSINON = 0.00001;  
  if ((x >= - EPSINON) && (x <= EPSINON) 这样判断是可取的
  至于为什么取0.00001,可以自己按实际情况定义


已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页