C/C++高频面试题

1:std的list. vector. map. set区别

我的答案:list基于链表, vector是数组, map红黑树(平衡二叉树), 这颗树具有对数据自动排序的功能, set也是红黑树(平衡二叉树), vector随机访问效率高, 对插入和删除效率较低, list基于链表自然对插入和删除效率高, 随机访问效率不高(需要遍历), map/set基于平衡二叉树, 是排序号的接口, 对插入和删除的效率高, 查询效率高. map是k,v
答案:map红黑树(平衡二叉树), 这颗树具有对数据自动排序的功能, set也是红黑树(平衡二叉树)
map:
底层就是红黑树(平衡排序二叉树)
键值保存,高效访问
插入效率低于链表,因为涉及排序

new和malloc区别?

我的回答 :1:malloc不会调用构造, malloc/free, new/delete, 会调用构造函数
答:

  1.   参数
    

使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸。

  1.   返回类型
    

new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。

  1.   分配失败
    

new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL。

  1.  自定义类型
    
      new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。
    
      malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
    
  2.  重载
    

C++允许重载new/delete操作符,特别的,布局new的就不需要为对象分配内存,而是指定了一个地址作为内存起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回此地址。而malloc不允许重载。

线程和进程区别?
我的回答:
线程是CPU最小调度单元, 进程中包含线程, 也是进程的最小调度单元. 进程的全局资源对进程内各个线程可见. 进程具有内存模型,

进程都有哪些资源

答:
1、虚拟地址空间

2、一个全局唯一的进程ID (PID)

3、一个可执行映像(image),也就是该进程的程序文件在内存中的表示

4、一个或多个线程

5、一个位于内核空间中的名为EPROCESS(executive process block,进程执行块)的数据结构,用以记录该进程的关键信息,包括进程的创建时间、映像文件名称等。

6、一个位于内核空间中的对象句柄表,用以记录和索引该进程所创建/打开的内核对象。操作系统根据该表格将用户模式下的句柄翻译为指向内核对象的指针。

7、一个位于描述内存目录表起始位置的基地址,简称页目录基地址(DirBase),当CPU切换到该进程/任务时,会将该地址加载到CR3寄存器,这样当前进程的虚拟地址才会被翻译为正确的物理地址。

8、一个位于用户空间中的进程环境块(Process Environment Block, PEB)。

9、一个访问权限令牌(access token),用于表示该进程的用户、安全组,以及优先级别。

gdb文件里都存了啥?

http://www.360doc.com/content/20/1229/11/73147761_954161882.shtml

答:总体来讲,GDB内部结构可分为两大块:

符号端,涉及到程序的符号信息。符号信息包含函数名,变量名,变量类型,行号,机器寄存器使用情况,等等。符号端将程序可执行文件中的符号信息提取出来,解析表达式,找到指定行号的内存地址,列出源代码,并大体上获取程序中的文本信息。
目标端,涉及到目标系统的操控。目标端包含了基本的调试工具,包括启动和终止程序,读取或修改内存和寄存器,捕捉信号,等等。这些工具的实现在不同的系统上可能会相差很大。大部分Unix类操作系统上都提供了一个系统函数ptrace,ptrace可以让一个进程读写另一个进程的状态。因此,GDB的目标端的主要工作就是调用ptrace和解析结果。对于嵌入式系统的交叉调试,过程有所不同,目标端通过数据线发送消息包,然后等待应答。

这两大模块相互较为独立,用户可以查看程序的代码,显示变量类型,但不需要实际地运行程序。反过来,不用符号信息完全使用机器码调试也是可能的。

将符号端和目标端连接起来的中间层是命令解释器和主程序运行控制循环

lib和dll区别,dll动态加载时在内存里是一份还是两份?

虚函数是怎么实现的(考察虚表)

C++实际模型是, 1 :对于一般继承是扩充已有存在的虚函数表; 2: 对于虚继承添加一个虚函数表指针
1: 多重或单继承时,扩充第一个继承的基类的虚函数表, 将所有基类虚函数表中已经重载的虚函数替换成派生类的虚函数地址.
2:多重继承时, 继承了多少个基类, 派生类的对象模型中就有多少个虚函数表指针, 派生类自身定义的虚函数则在第一个基类的虚函数表中

内联函数与宏定义函数区别?

使用宏和内联函数都可以节省在函数调用方面所带来的时间和空间开销。二者都采用了空间换时间的方式,在其调用处进行展开:
(1) 在预编译时期,宏定义在调用处执行字符串的原样替换。在编译时期,内联函数在调用处展开,同时进行参数类型检查。
(2) 内联函数首先是函数,可以像调用普通函数一样调用内联函数。而宏定义往往需要添加很多括号防止歧义,编写更加复杂。
(3) 内联函数可以作为某个类的成员函数,这样可以使用类的保护成员和私有成员。而当一个表达式涉及到类保护成员或私有成员时,宏就不能实现了(无法将this指针放在合适位置)。
(4) 内联函数会进行类型检查,宏定义函数不会进行类型检查。
(5)内联是编译器在调用处替换成等效代码, 而宏定义是原封不动的替换, 内联在函数很复杂时, 会被编译优化为不内联, 而宏定义不存在被优化.

可以用内联函数完全替代宏。
在编写内联函数时,函数体应该短小而简洁,不应该包含循环等较复杂结构,否则编译器不会将其当作内联函数看待,而是把它决议成为一个静态函数。

有些编译器甚至会优化内联函数,通常为避免一些不必要拷贝和构造,提高工作效率。

频繁的调用内联函数和宏定义容易造成代码膨胀,消耗更大的内存而造成过多的换页操作。

C++函数只声明,不定义会在哪一步报错?

调用时才会报错, 找不到符号, 链接器在程序运行时会进行连接实现, 也即函数入口, 因找不到函数入口而报错.
动态链接过程:
动态链接的基本思想是把程序按照模块拆分成各个相对独立的部分, 在程序运行时才将他们链接在一起形成一个完整的程序,而不是像静态链接一样把所有的程序模块都链接成一个单独的可执行的文件.
当程序被装载时, 也即在运行时, 系统的动态链接器会将程序所需要的所有动态链接库(除延迟加载外, 延迟加载也即只有在用到该库的时候才去装载)装载到进程的地址空间,并且将程序中所有未决议的符号绑定到相应的动态链接库中, 并进行重定位工作(重定位工作就是将动态库中的函数及变量, 根据地址偏移加载到进程的对应地址空间中)
动态库在运行时装载, 在编译的时候无法确定库的装载地址, 只有在运行时, 装载器会根据当前的地址空间的空闲情况, 动态分配一块足够大小的虚拟地址空间给动态库来装载.

extern “C”

的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。

5、shared_ptr,unique_ptr,weak_ptr

http状态码:

500 Internal Server Error 服务器内部错误,无法完成请求
501 Not Implemented 服务器不支持请求的功能,无法完成请求
502 Bad Gateway 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
503 Service Unavailable 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
504 Gateway Time-out 充当网关或代理的服务器,未及时从远端服务器获取请求
505 HTTP Version not supported 服务器不支持请求的HTTP协议的版本,无

400 Bad Request 客户端请求的语法错误,服务器无法理解
401 Unauthorized 请求要求用户的身份认证
402 Payment Required 保留,将来使用
403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求
404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
405 Method Not Allowed 客户端请求中的方法被禁止
400-405?

http 1与2有哪些改进?

我们现在用的是POST /iac/securityService/v1/negotiation HTTP/1.1,

http1.1的长连接和http2.0的多路复用的区别?
http1.1之前一次请求就会建立一次连接,http1.1通过管道化方式实现多个请求串行化共享同一个连接,但是一旦某个请求耗时严重,就导致后面的请求被阻塞,俗称线头阻塞;
http2.0多个请求共享同一个连接,并且是并行的,所有请求不会影响其他请求;

1996年  HTTP/1.0 默认短连接(一次请求建议一次TCP连接,请求完就断开),支持GET、POST、 HEAD请求

1999年  HTTP/1.1 1:默认长连接keep_alive, 2:引入了管道机制( pipelining),即在同一个TCP连接里,客户端可以同时发送多个
请求,进一步改进了HTTP协议的效率 3:支持PUT、DELETE、PATCH等六种请求
        4:增加host头,支持虚拟主机;支持断点续传功能

2015年  HTTP/2.0 多路复用,降低开销(一次TCP连接可以处理多个请求);

服务器主动推送(相关资源一个请求全部推送);

解析基于二进制,解析错误少,更高效(HTTP/1.X解析基于文本);

报头压缩,降低开销。

C中char字符串和C++ 的string的区别和联系?

答:
string 是定义一个字符串,存储的是一段如“abcd”的数据,而且最后还有一个结束符’\0’;
char 是定义一个字符,存储一个字符,占一个字节

std::unordered_map/std::map的区别----------?

对智能指针有多少了解?

std::weak_ptr如何转std::shared_ptr
std::weak_ptr::lock存在的意义是什么
答: _task_manager.lock()获取, TaskManagerSp task_manager = _task_manager.lock();
解决嵌套引用或者菱形引用 ptrA = ptrB; ptrB = ptrA; 这是如果都用智能指针那么会导致引用计数无法为0, 对象无法释放, 改用弱引用不会引起应用计数的改变而又可以获取到该指针, 从而可以解决智能指针相互引用的问题.

内存池了解过吗,为什么要有内存池

答:
1:避免频繁调用内核内存分配和释放的接口, 从而减少了用户态到内核态的转换, 也减少了内存从内核到应用的频繁来回拷贝.
2:一次性向系统申请大内存, 可以避免内核来管理频繁分配内存导致的大量内存碎片, 大量的内存碎片, 内核管理起来费劲. 由用户来管理, 保存一定固定空闲内存避免内存的频繁申请与释放.
什么是内存碎片,为什么会有内存碎片,如何处理内存碎片
答:在linux内存是分页管理的, 在32位操作系统中, 分配内存的最小单元为页, 一页4kb, 而程序中大部分场景分配内存是小于4KB的, 那么内存页中剩下的未分配的即为碎片.

如何处理内存碎片?

首先得要明白两个概念:
内碎片:指同一个页面内的碎片化
外碎片:外碎片就是以页为单位的内存之间的碎片化

伙伴关系算法: 解决外碎片问题, 负责大块连续内存的分配和释放, 以页为单位
伙伴关系算法:
伙伴关系算法把所有的空闲页面分成MAX_ORDER(MAX_ORDER默认大小为11)个块链表每个链表中的一个节点指向一个含有2的幂次个页面的块,即页块或简称块
伙伴:
大小相同、物理地址连续的两个页块称为伙伴.
伙伴算法工作原理:
首先在大小满足要求的块链表中查找是否有空闲块,若有则直接分配,否则在更大的块的块链表中查找, 找到了则把更大的块进行拆分,比如256kb拆分成两个128kb, 这个两个连续的块即为伙伴,一个伙伴分配给用户, 一个伙伴插入到128kb的链表块中;
逆过程是块的释放,此时会把满足伙伴关系的块合并,组成一个更大的块,并插入到相应的块链表中

Slab分配器(slab对象管理器): 解决内碎片问题, 负责小内存的申请, slab机制避免内碎片问题.

快速排序与堆排序?

第一、堆排序访问数据的方式没有快速排序友好。
对于快速排序来说,数据是顺序访问的。而对于堆排序来说,数据是跳着访问的。比如,堆排序中,最重要的一个操作就是数据的堆化。比如下面这个例子,对堆顶进行堆化,会依次访问数组下标是1,2,4,8的元素,而不像快速排序那样,局部顺序访问,所以,这样对CPU缓存是不友好的。

第二、对于同样的数据,在排序过程中,堆排序算法的数据交换次数要多于快速排序。

我们在讲排序的时候,提过两个概念,有序度和逆序度。对于基于比较的排序算法来说,整个排序过程是由两个基本操作组成的,比较和交换。快速排序交换的次数不会比逆序度多。
但是堆排序的第一步是建堆,建堆的过程会打乱数据原有的相对选择顺序,导致数据有序度降低。比如对于一组已经有序的数据来说,经过建堆之后,数据反而变得更无序了。

大数据量(内存够用)下,快排与堆排序的对比(考察缓存命中率的对比)

缓存失效,替换原理

答:常用的缓存淘汰策略有以下:
先进先出算法(FIFO)
Least Frequently Used(LFU)
淘汰一定时期内被访问次数最少的页面,以次数作为参考
Least Recently Used(LRU)
淘汰最长时间未被使用的页面,以时间作为参考
//https://www.cnblogs.com/zhangfengshi/p/11466263.html

32位系统运行大于4G的程序,如何寻址(考察虚拟内存,虚拟地址空间)

数据库中文件在磁盘上的存储方式

B树

数据库索引存储结构中采用B树与二叉树的区别?

https://zhuanlan.zhihu.com/p/92405269

字符串实现减法

CPU调度方式

1:FIFO
2:优先级
3:轮询

linux进程调度算法

1:先来先服务(FCFS)
是最简单的调度算法,按先后顺序进行调度。
2:高优先权优先调度算法
3:基于时间片的轮转调度算法
4:多级反馈队列调度算法:

map是怎么实现的, unordered_map 又是怎么实现的呢?

红黑树, 排序, 见第一个问题阐述.

你可能感兴趣的:(基础知识及面试)