牛客网错题集锦之一

目录

1. 派生类和继承

2. 指针数组

3. 二叉树遍历

4. UTF-8和UTF-16

5.构造函数和析构函数的调用顺序

6. 内部静态类的可见性和存在性不一致

7. 用C程序实现的算法可以没有输入但必须要有输出

8. Nagle算法的规则:


1. 派生类和继承

  • 派生类类内,不管哪种继承,一定能访问基类的public和protected成员,永远不能访问private成员;
  • 派生类对象,只有public派生时,才能访问基类的public成员;
  • 派生类对象中含有从基类继承来的成员,但是派生类并不总能直接对其访问和修改。

2. 指针数组

int c[4][5],(*p)[5];p=c;

整理为:

int c[4][5];
int* p[5];
p=c;

指针p为包含5个指针地址的指针数组。

3. 二叉树遍历

  • 前序遍历:根结点 ---> 左子树 ---> 右子树
  • 中序遍历:左子树---> 根结点 ---> 右子树
  • 后序遍历:左子树 ---> 右子树 ---> 根结点

4. UTF-8和UTF-16

  • 一个汉字UTF-8 占用3个字节, UTF-16 占用2个字节。
  • 存储文本需要在文本使用 EF BB BF 三个字节表示使用 UTF-8 编码,使用 FE FF 表示使用 UTF-16 编码。
  1. UTF-8编码的时候,占用空间为: 字母数 + 汉字数*3 + 3,UTF-8的空间是根据保存的内容不同而不同。

  2. UTF-16编码的时候,占用空间为: 字符数*2 + 2

  • 0xxxxxxx 一个字节兼容ASCII,能表示127个字符
  • 110xxxxx 10xxxxxx.如果是这样的格式,则把两个字节当一个字符
  • 1110xxxx 10xxxxxx 10xxxxxx 如果是这种格式则是三个字节当一个字符

5.构造函数和析构函数的调用顺序

  1. 构造函数的调用顺序是:基类>对象>派生类,析构顺序相反;
  2. 构造函数里初始化列表初始化顺序由成员变量的声明顺序决定。

6. 内部静态类可见性存在性不一致

  • 可见性:指哪里可以调用,能调用就是可见的
  • 存在性:指什么时候初始化,什么时候释放,指生命周期

7. 用C程序实现的算法可以没有输入必须要有输出

一个算法应该具有以下5个特性:有穷性、确定性、可行性、有零个或多个输入、有一个或多个输出。

因此一个算法可以没有输入(程序的功能确定),但必须要有输出,没有输出的算法是没有意义的。

8. Nagle算法的规则:

  1. 如果包长度达到MSS,则允许发送;
  2. 如果该包含有FIN,则允许发送;
  3. 设置了TCP_NODELAY选项,则允许发送;
  4. 未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;
  5. 上述条件都未满足,但发生了超时(一般为200ms),则立即发送。

Nagle算法只允许一个未被ACK的包存在于网络,它并不管包的大小,因此它事实上就是一个扩展的停-等协议,只不过它是基于包停-等的,而不是基于字节停-等的。Nagle算法完全由TCP协议的ACK机制决定,这会带来一些问题,比如如果对端ACK回复很快的话,Nagle事实上不会拼接太多的数据包,虽然避免了网络拥塞,网络总体的利用率依然很低。

Nagle算法是silly window syndrome(SWS)预防算法的一个半集。SWS算法预防发送少量的数据,Nagle算法是其在发送方的实现,而接收方要做的是不要通告缓冲空间的很小增长,不通知小窗口,除非缓冲区空间有显著的增长。这里显著的增长定义为完全大小的段(MSS)或增长到大于最大窗口的一半。
注意:BSD的实现是允许在空闲链接上发送大的写操作剩下的最后的小段,也就是说,当超过1个MSS数据发送时,内核先依次发送完n个MSS的数据包,然后再发送尾部的小数据包,其间不再延时等待。(假设网络不阻塞且接收窗口足够大)

举个例子,比如之前的blog中的实验,一开始client端调用socket的write操作将一个int型数据(称为A块)写入到网络中,由于此时连接是空闲的(也就是说还没有未被确认的小段),因此这个int型数据会被马上发送到server端,接着,client端又调用write操作写入‘\r\n’(简称B块),这个时候,A块的ACK没有返回,所以可以认为已经存在了一个未被确认的小段,所以B块没有立即被发送,一直等待A块的ACK收到(大概40ms之后),B块才被发送。

这里还隐藏了一个问题,就是A块数据的ACK为什么40ms之后才收到?这是因为TCP/IP中不仅仅有nagle算法,还有一个TCP确认延迟机制 。当Server端收到数据之后,它并不会马上向client端发送ACK,而是会将ACK的发送延迟一段时间(假设为t),它希望在t时间内server端会向client端发送应答数据,这样ACK就能够和应答数据一起发送,就像是应答数据捎带着ACK过去。在我之前的时间中,t大概就是40ms。这就解释了为什么'\r\n'(B块)总是在A块之后40ms才发出。
当然,TCP确认延迟40ms并不是一直不变的,TCP连接的延迟确认时间一般初始化为最小值40ms,随后根据连接的重传超时时间(RTO)、上次收到数据包与本次接收数据包的时间间隔等参数进行不断调整。另外可以通过设置TCP_QUICKACK选项来取消确认延迟。

1. TCP_NODELAY 选项

默认情况下,发送数据采用Nagle 算法。这样虽然提高了网络吞吐量,但是实时性却降低了,在一些交互性很强的应用程序来说是不允许的,使用TCP_NODELAY选项可以禁止Nagle 算法

2. TCP_CORK 选项

设置该选项后,内核会尽力把小数据包拼接成一个大的数据包(一个MTU)再发送出去,当然若一定时间后(一般为200ms),内核仍然没有组合成一个MTU时也必须发送现有的数据。

然而,TCP_CORK并不会将连接完全塞住。内核其实并不知道应用层到底什么时候会发送第二批数据用于和第一批数据拼接以达到MTU的大小,因此内核会给出一个时间限制,在该时间内没有拼接成一个大包(努力接近MTU)的话,内核就会无条件发送。也就是说若应用层程序发送小包数据的间隔不够短时,TCP_CORK就没有一点作用,反而失去了数据的实时性(每个小包数据都会延时一定时间再发送)。

3. Nagle算法与CORK算法区别

Nagle算法和CORK算法非常类似,但是它们的着眼点不一样:

  • Nagle算法主要避免网络因为太多的小包(协议头的比例非常之大)而拥塞
  • Nagle算法关心网络拥塞问题,只要所有的ACK回来则发包;
  • CORK算法则是为了提高网络的利用率,使得总体上协议头占用的比例尽可能的小。
  • CORK算法关心内容,在前后数据包发送间隔很短的前提下(很重要,否则内核会帮你将分散的包发出),即使你是分散发送多个小数据包,你也可以通过使能CORK算法将这些内容拼接在一个包内,如果此时用Nagle算法的话,则可能做不到这一点。

如此看来这二者在避免发送小包上是一致的,在用户控制的层面上,Nagle算法完全不受用户socket的控制,你只能简单的设置TCP_NODELAY而禁用它,CORK算法同样也是通过设置或者清除TCP_CORK使能或者禁用之。

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值