面试官:讲一下A与B的区别

网络

TCP和UDP的区别

  1. UDP:无连接,不可靠,面向数据报。UDP采用面向数据报传输方式传输数据,没有保证可靠传输机制,丢包不处理。应用层给传输层多么大的数据块,传输层原模原样整块发送,因此UDP数据包需要在应用层进行分包操作。UDP优点是:传输速度快,适用于实时性比较强对数据可靠性要求不高的通信场景。
  2. TCP:需要经过三次握手建立连接,TCP有确认应答机制、序号和确认序号、超时重传可以保证可靠传输,TCP面向数据流,意味着,TCP没有长度限制,收发数据灵活,但同时TCP可能出现粘包问题。

UDPsendto怎么保证对端收到了?
UDP协议本身不具备这样的功能,如果要实现UDP保证对端接受到发送的消息,可以在应用层实现UDP的确认应答机制和超时重传机制。

http2 和http1的区别

  1. HTTP2 基于二进制进行解析 , HTTP1.x基于文本解析,文本的格式跟多,考虑到健壮性的场景会有很多,而http2统一按照二进制进行解析,方便且健壮性好
  2. 多路复用,即连接共享,即每一个request都是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。
  3. header报头压缩,通信双方缓存各持有1份header fields,即避免了重复header的传输,有减小了header传输的大小.
  4. 服务端推送,简单说就是server端能够提前给请求端发送多条数据.比如client请求资源a,server端将a,b,c都发给了client.那么下次client要是请求b,c就可以直接从缓存读取,不用发起请求.

http1.0 和http1.1的区别

  1. 缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
  2. 带宽优化及网络连接的使用,http1.0不支持资源部分发送,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content)。
  3. 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
  4. Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。HTTP1.1的请求消息和响应消息都应支持Host头域。
  5. 长连接,HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。

http 和https的区别

  1. HTTPS协议需要到CA申请证书,一般免费证书很少,需要交费。
  2. HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的。
  3. HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  4. HTTPS可以有效的防止运营商劫持,解决了防劫持的一个大问题

POST和GET的区别

  1. GET请求参数放在URL中,POST放在request body中。
  2. GET请求只能进行url编码,POST请求支持多种编码方式。
  3. GET请求在URL中传递的参数是有长度限制的,而POST没有。
  4. GET比POST更不安全,因为参数直接暴露在URL中,所以不能传递敏感信息。
  5. GET只发一次TCP连接,POST发送两次TCP连接。对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据); 而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。

select和epoll的区别

  1. 描述符数量限制:select采用位图可以同时监控的描述符有上限,epoll没有文件描述符数量限制
  2. 数据拷贝轻量:每次调用select, 都需要手动设置fd集合, 每次调用select,都需要把fd集合从用户态拷贝到内核态,同时每次调用select都需要在内核遍历传递进来的所有fd。epoll是需要一次用户态到内核态、一次内核态到用户态的拷贝。
  3. 接口使用方便: 将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd。epoll不需要每次循环都设置关注的文件描述符。
  4. 事件回调机制:epoll底层采用红黑树挂载epoll事件对象,而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当响应的事件发生时会调用这个回调函数。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有就绪事件即可.如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户. 这个操作的时间复杂度是O(1)。
  5. 拓展知识:向fd_set位图中添加描述符,就是将这个描述符对应位位置1。select采用轮循判断,如果有事件在超时时间内就绪了,select就立即返回,select返回的是描述符的个数。就绪的描述符在fd_set这样的位图中,没有就绪的位图,会被select剔除,遍历这个位图,还存在这个位图的描述符就是就绪描述符。
  6. 不同与select使用三个位图来表示三个fdset的方式,poll使用一个pollfd的指针实现。

水平触发和边缘触发的区别

  1. epoll默认采用LT(level)水平触发epoll_wait检测到事件就绪,可以不立即处理,或者只处理一部分,epoll_wait就立即返回了,第二次调用epoll_wait时候,epoll_wait再返回,触发事件,再进行数据处理。直到缓冲区的数据都处理完了,epoll_wait才不会立即返回。
  2. ET(Edge)边缘触发,一旦有事件就绪了,就必须立即处理,如果数据未完全处理,第二次调用epoll_wait,epoll_wait不会再返回。也就是说边缘触发数据只有一次处理机会,边缘触发只支持非阻塞的读写。

操作系统

进程和线程的区别

  1. 地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
  2. 资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、cpu、打开文件,信号等(线程也有各自独有的数据栈、信号屏蔽字、 上下文数据、errno),但是进程之间的资源是独立的
  3. 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
  4. 进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程
  5. 执行过程:每个独立的进程程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
  6. 线程是处理器调度的基本单位,进程是分配资源和管理资源的基本单位。
  7. 两者均可并发执行。

何时使用多进程,何时使用多线程?
对资源的管理和保护要求高,不限制开销和效率时,使用多进程。
要求效率高,频繁切换时,资源的保护管理要求不是很高时,使用多线程。

静态库和动态库区别

  1. 静态库在程序的链接阶段被复制到了可执行程序中,每一个调用该静态库的程序都有一份该静态库代码的拷贝。
  2. 动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。使用动态库的优点是系统只需载入一次动态库,不同的程序可以得到内存中相同的动态库的副本,因此节省了很多内存。

栈和堆的区别

  • 从静态存储区域分配:它是由编译器自动分配和释放的,即内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,直到整个程序运行结束时才被释放,如全局变量与 static 变量。
  1. 栈和堆的资源分配都属于动态分配,都由alloca 函数实现。
  2. 在栈上分配:它同样也是由编译器自动分配和释放的,即在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元将被自动释放。需要注意的是,栈内存分配运算内置于处理器的指令集中,它的运行效率一般很高,但是分配的内存容量有限。
  3. 从堆上分配:也被称为动态内存分配,它是由程序员手动完成申请和释放的。如果在堆上分配了内存空间,就必须及时释放它,否则将会导致运行的程序出现内存泄漏等错误。
  4. 频繁分配和释放(malloc / free)不同大小的堆空间势必会造成内存空间的不连续,从而造成大量碎片,导致程序效率降低;而对栈来讲,则不会存在这个问题。
  5. 堆的机制也相当复杂。例如,为了分配一块堆内存,首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆节点,然后将该节点从空闲节点链表中删除,并将该节点的空间分配给程序。而对于大多数系统,会在这块内存空间的首地址处记录本次分配的大小,这样,代码中的 delete 语句才能正确释放本内存空间。另外,由于找到的堆节点的大小不一定正好等于申请的大小,系统会自动将多余的那部分重新放入空闲链表中。很显然,堆的分配效率比栈要低得多。

C++

struct和class的区别

默认继承权限不同,class继承默认是private继承,而struct默认是public继承。

引用和指针的区别

  1. 指针是变量,变量内容存储的是指针指向的变量的地址。引用是变量的别名。
  2. 引用在定义时候必须初始化,指针没有这样的要求,指针还可以指向NULL,引用不可以。
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体。
  4. sizeof引用变量=sizeof引用实体大小,sizeof指针大小=4(32位系统) or 8(64位系统)。
  5. 引用比指针更加安全,引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小。

vector和list的区别

  1. vector采用连续存储,支持下标随机访问。
  2. vector的插入和删除可能存在数据块的拷贝移动,时间复杂度是O(n)。
  3. vector增容可能会导致数据块拷贝移动,代价很大。
  4. vector还存在空间浪费问题,vector采用2倍增容(Linux系统采用1.5倍增容)。使用reserve函数可以修改vector空间,造成不必要的增容扩空间带来的拷贝。
  5. 双向链表list只能通过指针访问,不支持随机访问,内存空间不连续。优点是插入删除效率效率高于vector。

组合和继承的区别

  1. public继承方式遵循“is a”原则,属于继承。
  2. private和protected继承遵循“has a”原则,属于组合。
  3. 当组合和继承都可以使用的情况下,选择组合。开发应该遵循“高内聚低耦合”原则。

new和malloc的区别

  1. new=对象构造函数+operator new(malloc)
  2. delete=对象析构函数+operator delete(free)
  3. new type[size],底层是通过调用operator new一次性开辟所需要的空间大小,如果自定义类型定义了析构函数,那么operator new会多开辟四个字节存储size。

map和unordered_map的区别

  1. map和unordered_map都是基于Key_Value存储结构,都可以通过key查询到唯一的value,都可使用下标[]访问。
  2. map底层采用红黑树实现,map内部自建一颗红黑树(一种非严格意义上的平衡二叉树),所以在map内部所有的数据都是有序的,且map的查询、插入、删除操作的时间复杂度都是O(logN)。在使用时,map的key需要定义operator<。
  3. unordered_map的底层是一个防冗余的哈希表(采用除留余数法)。unordered_map不会根据key的大小进行排序,存储时是根据key的hash值判断元素是否相同,即unordered_map内部元素是无序的。unordered_map的key需要定义hash_value函数并且重载operator==。哈希表最大的优点,就是把数据的存储和查找消耗的时间大大降低,时间复杂度为O(1);而代价仅仅是消耗比较多的内存。

猜你喜欢

转载自blog.csdn.net/Vickers_xiaowei/article/details/92805031