c++面试题目小结
-
char指针多大
回答:char1,short2,int4,指针4,long4,longlong8,double8.强调了我知道64位一般指针是8,但是我自己在编译器里sizeof确实是这个结果。 -
char指针指向int,并且输出会c%输出嘛?变成s%呢。
回答:否,除非强制转换,还不能太大。s%不知道在哪儿分界 -
了解namspace std什么意思吗?
所谓namespace,是指标识符的各种可见范围;C++标准程序库中的所有标识符都被定义于一个名为std的namespace中;
<iostream>
和<iostream.h>
是不一样,前者没有后缀,这二者实际上是两个文件。带有后缀.h的在c++中不再使用,其相当于在c中调用库函数,使用的是全局命名空间;
当使用< iostream>的时候,该头文件没有定义全局命名空间,必须使用namespace std -
vector clear操作会不会释放内存:delete和clear操作都不会释放内存。vector的内存释放实在vector析构的时候进行的,即使vector元素全部被delete,其占据的内存空间还是保留。vector的内存会根据插入元素自动生长,但不会回收内存;
-
vector erase操作注意事项:
vector<int> vi;
for(auto iter=vi.begin();iter!=vi.end();iter++)
{
vi.erase(iter);
}
1)返回的是所删除位置的下一个位置的指针,iter本身变为野指针。此时不能对iter进行操作,因此上面代码会报错。
改为: iter=vi.erase(iter);
2)上述代码还存在一个问题:返回的iter指向删除位置的下一个位置,此时iter不能再进行++操作;
可以改为:
vector<int> vi;
for(auto iter=vi.begin();iter!=vi.end();)
{
iter=vi.erase(iter);
}
-
虚函数表的实现原理:
虚函数表存储一个类的虚函数的地址,包括类本身以及父类(所有继承的类)的虚函数的地址。如果子类重写了父类的虚函数,在虚函数表中会把对应的虚函数地址替换为子类的函数的地址。
每个类都有一个虚函数指针,指向虚函数表。虚函数表指针放在对象的开始地址处,指向对象所在类的虚函数表的地址。在多继承情况下(第一个基类作为主类),会存在多个虚函数表指针,分别指向对应不同基类的虚函数表 -
虚函数表的存放位置,原因:
存放在静态区。虚函数表是一个类所共有的,不属于某个对象。
c/c++程序所占用的内存一共分为五种::堆区、栈区、程序代码区、静态区、文字常量区 -
各种排序算法(所有能记得的)的时间复杂度和基本实现原理(重点问了快排和堆排序):
堆排序和快速排序过程以及最好最坏情况的分析,多写几遍代码吧 -
进程与线程:
进程与线程的区别与联系、进程与线程的通信方式 -
TCP报文首部格式?
序列号seq:占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上一个序号,第一个字节的编号由本地随机产生;给字节编上序号后,就给每一个报文段指派一个序号;序列号seq就是这个报文段中的第一个字节的数据编号。确认号ack:占4个字节,期待收到对方下一个报文段的第一个数据字节的序号;序列号表示报文段携带数据的第一个字节的编号;而确认号指的是期望接收到下一个字节的编号;因此当前报文段最后一个字节的编号+1即为确认号。
确认ACK:占1位,仅当ACK=1时,确认号字段才有效。ACK=0时,确认号无效
同步SYN:连接建立时用于同步序号。当SYN=1,ACK=0时表示:这是一个连接请求报文段。若同意连接,则在响应报文段中使得SYN=1,ACK=1。因此,SYN=1表示这是一个连接请求,或连接接受报文。SYN这个标志位只有在TCP建连接时才会被置1,握手完成后SYN标志位被置0。
终止FIN:用来释放一个连接。FIN=1表示:此报文段的发送方的数据已经发送完毕,并要求释放运输连接
-
tcp三次握手四次挥手的过程以及一些细节
三次握手:
-
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
-
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
-
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手
- 第一次挥手:客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入**FIN-WAIT-1(终止等待1)**状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
- 第二次挥手:服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
- 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
- 第三次挥手:服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
- 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过**2∗∗MSL(最长报文段寿命)**的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
- 第四次挥手:服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
-
为什么连接的时候是三次握手,关闭的时候却是四次握手?
因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手 -
DNS了解吗
回答:DNS是域名系统;应用层软件一般直接使用的是域名而非IP地址,在网络层进行数据传输时才将域名转化为路由中使用的IP地址;大部分域名都在本地进行解析,只有少数域名需要在互联网上进行解析;
互联网域名结构:层次树状结构;
域名服务器具有四种类型:根域名服务器,顶级域名服务器,权限域名服务器,本地域名服务器;
查询方式:主机向本地域名服务器查询,采用递归查询;本地域名服务器向顶级服务器查询,使用迭代查询; -
ip协议清楚吗?
IPv4:
IPv6:
1)取消了首部长度,因为IPv6的首部长度是固定40个字节。
2)取消了服务类型,因为流标号和优先级结合起来实现了服务类型的功能。
3)取消了总长度字段,改用为有效载荷长度,有效载荷就是后面的扩展首部加上数据报中的数据。
4)取消了标识,标志和片偏移,因为这些功能都包含在了扩展首部里面。
5)取消了协议字段,改用为下一个首部,功能不变,这样更容易理解。
6)取消了生存时间ttl,改用为跳数限制,功能不变,这样更容易理解,更形象了。
7)取消了首部效验和,这样加快了路由器对数据报的处理速度,在数据链路层中,当我们发现有差错的帧就会抛弃,在运输层中,在udp中,当发现有差错就会抛弃,在tcp中,当发现有差错就会重传,直到传送到目的进程为止。因此在网路层的检测就可以精简掉。
8)取消了选项字段,功能归并在了扩展首部上。 -
用户态、内核态的区别,为什么要设计用户态和内核态
( 用户态和内核态的理解和区别)
- 当一个进程在执行用户自己的代码时处于用户运行态(用户态),此时特权级最低,为3级,是普通的用户进程运行的特权级,大部分用户直接面对的程序都是运行在用户态;Ring3状态不能访问Ring0的地址空间,包括代码和数据;
- 当一个进程因为系统调用陷入内核代码中执行时处于内核运行态(内核态),此时特权级最高,为0级。执行的内核代码会使用当前进程的内核栈,每个进程都有自己的内核栈。
- 目的:用户态的程序就不能随意操作内核地址空间,具有一定的安全保护作用。这说的保护模式是指通过内存页表操作等机制,保证进程间的地址空间不会互相冲突,一个进程的操作不会修改另一个进程地址空间中的数据。
- 切回内核态的场景
当在系统中执行一个程序时,大部分时间是运行在用户态下的,在其需要操作系统帮助完成一些用户态自己没有特权和能力完成的操作时就会切换到内核态
三种情况:
- 系统调用
这是用户态进程主动要求切换到内核态的一种方式。用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作。例如fork()就是执行了一个创建新进程的系统调用。系统调用的机制和新是使用了操作系统为用户特别开放的一个中断来实现,如Linux的int 80h中断 - 异常
当cpu在执行运行在用户态下的程序时,发生了一些没有预知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关进程中,也就是切换到了内核态,如缺页异常 - 外部中断
当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令而转到与中断信号对应的处理程序去执行,如果前面执行的指令时用户态下的程序,那么转换的过程自然就会是 由用户态到内核态的切换。如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后边的操作等
这三种方式是系统在运行时由用户态切换到内核态的最主要方式,其中系统调用可以认为是用户进程主动发起的,异常和外围设备中断则是被动的
- 海量数据如何找到最大的k个数(堆)
https://blog.csdn.net/hanjing_1995/article/details/51539593
https://blog.csdn.net/qq1404510094/article/details/80323168
参考: