
文章目录零、C语言基础1.const 关键字2.static 关键字一、C基础1.指针和引用2.智能指针用过哪些智能指针举例说明(1)unique_ptr(2)shared_ptr如何实现的(3)weak_ptr3.运算符4.虚函数原理虚函数是什么工作机制是什么5.多态6.STL7.设计模式 2种(1)单例模式(2)工厂模式8.现代C11/14/17(1)C11有哪些新特性请举例说明9.多线程10.网络编程11.拷贝构造函数的三种调用时机12.友元的三种形式13.运算符重载的三种形式14.初始化顺序、赋值顺序15.重载、隐藏、覆盖16.构造函数17.函数指针与指针函数二、Linux命令三、Git命令1.上传add、commit、push2.下拉git pull3.查看分支git branch4.下载仓库5.查看分支6.切换分支7.更新子仓库四、C构建工具链(Build Toolchain)、构建命令五、Python和Shell六、操作系统1.32位系统的内存布局2.死锁3.用户态和内核态、宏内核与微内核七、网络编程 / 计算机网络1.TCP、UDP区别2.Socket通信过程3.三次握手4.四次挥手八、数据库1.数据库的事务和索引九、Qt1.Qt信号与槽机制2.Qt元对象3.Qt 事件机制 或者 事件循环4.GDB调试、你一般都用QtCreator调试嘛十、手撕算法1.二分查找2.LRU Cache3.滑动窗口4.排序(1)手撕快速排序(2)冒泡降序排序5.四维图新面试算法题零、C语言基础1.const 关键字C/C关键字consthttps://blog.csdn.net/edward1027/article/details/1368549672.static 关键字C/C关键字statichttps://blog.csdn.net/edward1027/article/details/136849197一、C基础1.指针和引用问题1指针的定义是什么指针的定义指针是一个变量该变量存储的是一个地址指向内存的一个存储单元。【指针就是存储内存地址的变量。】问题2引用的定义和本质是什么(1)引用的定义引用是变量的别名引用一经初始化就和目标变量绑定代表同一内存单元。(2)引用本质引用底层本质是常量指针被编译器自动封装的常量指针Type *constint*constra;问题3指针和引用的区别是什么各自适合什么场景1.区别(1)表达符号上指针是T *t引用是T t(2)指针可以为空可以重新指向引用必须初始化且不能为空不能重新绑定(换绑)(3)指针可以有多级引用只能是一级intnum100;int*pnum;//指针intrefnum;//引用cout*pendl;//*p num, p numcoutrefendl;*p200;//修改num的值pnullptr;//指针可以重新指向别的地址ref300;//修改num的值//ref一旦绑定变量永远不能再绑定其他变量2.使用场景(1)原则能使用引用就优先用引用必须用指针的时候再用指针。1引用语法更接近变量本身底层实现是指针地址。函数传参时引用更安全指针适合需要动态改变指向或可能为空的场景(2)必须用指针不能用引用的情况1需要代表 “空”不存在对象对象可能不存在允许 “空”函数入参对象可能不存在函数返回有可能找不到对象2需要中途更换指向目标需要运行时改变指向的目标 (引用不能重定向指针可以随时重新赋值指向别的对象)3 动态内存管理new /delete堆上创建对象天然返回指针4容器存储支持指针容器里面不能存引用vectorNode* nodes 合法vectorint 非法现代 C 补充重要升级工程开发中尽量少用裸指针 T*需要所有权std::unique_ptr需要共享所有权std::shared_ptr2.智能指针用过哪些智能指针举例说明C11提出智能指针是为了实现自动化的内存管理防止内存泄露。因为C没有垃圾回收机制(1)unique_ptrunique_ptr是独占型智能指针unique_ptr没有额外开销#includememoryunique_ptrintpmake_uniqueint(100);(2)shared_ptr如何实现的shared_ptr是共享型智能指针指向对象有引用计数引用计数为0时自动delete 类似 操作系统-文件管理-索引结点-引用计数。但引用计数会增加一些内存开销而unique_ptr就没有内存开销。shared_ptrintpmake_sharedint();//括号内不填数值,默认初始化为0(3)weak_ptrweak_ptr是一种不控制对象生命周期的指针专门与shared_ptr配合使用。它不会增加引用计数主要用于解决循环引用问题。weak_ptr可以用来检查指向的资源是否还存在避免悬挂指针的风险。3.运算符1.·和-成员访问运算符 和 箭头成员访问运算符对象.成员 指针-成员ptr-member// 等价于(*ptr).member4.虚函数原理虚函数是什么工作机制是什么1.概念虚函数是在基类中使用virtual关键字声明的成员函数。它允许派生类重写/覆盖(override)基类的同形虚函数(函数名、参数列表、返回值都相同的虚函数)。2.作用/目的/干什么的/为什么要用到虚函数(1)提出虚函数的目的就是为了实现动态多态 (运行时多态)。(2)基类指针指向派生类对象(或基类引用绑定派生类对象)就可调用派生类的虚函数。父类指针 / 引用指向子类对象时自动调用子类重写的函数而不是父类版本。3.原理虚函数的实现依赖于虚函数表 vtable 和 虚函数指针 vfptr。总结对象中的虚函数指针指向虚函数表。虚函数表中存放的是函数的入口地址指向程序代码区中虚函数的存储位置。(1)虚函数指针虚函数指针指向虚表的首地址。vptr占8个字节。vptr是类的隐藏变量在构造函数中被初始化为虚函数表的入口地址。(2)虚函数表(虚表)存放虚函数的入口地址①每个包含虚函数的类都有一个独立的虚函数表(vtable)同一个类的所有对象共享一个虚函数表。②虚函数表包含该类的所有虚函数的入口地址。③虚函数表是一个函数指针数组数组的每个元素都是一个指向虚函数的指针(地址)存放虚函数的入口地址。④每个类有一个虚函数表类的所有实例化对象共享这一张虚表。(3)调用虚函数通过指针调用虚函数通过虚函数指针找到虚表。(动态绑定)通过对象调用虚函数直接去程序代码区中找不通过虚表。(4)存放规则①虚函数表中虚函数的排列顺序是按照虚函数在基类中的声明顺序排布的。②派生类继承时会创建一张虚表并复制基类虚表中的所有内容。若派生类自己也有虚函数则在虚表中往后排放。若派生类中发生覆盖则覆盖虚表中虚函数的入口地址。(动态多态基类指针指向派生类对象多态时查看的是派生类的虚表若发生覆盖就调用派生类覆盖的虚函数)③在带虚函数的多继承的结构下虚表有多张1第一张虚表进行覆盖后面几张虚表应该发生覆盖的对应位置存放go to指令跳转到第一张虚表 (vs的存放规则)。2派生类中内存布局基类子对象按照继承的先后顺序排放。若有定义虚函数的类会优先排放其虚函数指针和基类子对象。//虚函数与多态#includeiostreamusingstd::cout;usingstd::endl;//基类classAnimal{public:virtualvoidmakeSound()const{coutAnimal makes a sound.endl;}};//派生类:DogclassDog:publicAnimal{public:virtualvoidmakeSound()constoverride{coutDog barks.endl;}};//派生类:CatclassCat:publicAnimal{public:virtualvoidmakeSound()constoverride{coutCat meows.endl;}};intmain(){//基类指针指向派生类对象Animal*animal1newDog();Animal*animal2newCat();//通过基类指针调用派生类的虚函数animal1-makeSound();animal2-makeSound();return0;}更详细内容C(week12): C基础 第八章:多态5.多态1.多态的含义多态意为“多种形态”即“一个接口多种实现”。用一个统一的接口指针或引用来操作不同的对象并产生不同的行为2.多态的分类①编译时多态(静态多态)是通过函数重载、运算符重载实现的。②运行时多态(动态多态)是基类指针指向派生类对象(或基类引用绑定派生类对象)从而能调用派生类覆盖的虚函数。通过继承和虚函数实现。3.动态多态激活的5个条件①基类定义了虚函数或声明了纯虚函数②派生类中覆盖虚函数 (覆盖的是派生类的虚函数表中的虚函数的入口地址③创建派生类对象④基类的指针指向派生类对象或基类引用绑定派生类对象⑤通过基类指针或引用调用虚函数4.多态的效果基类指针可以调用到派生类的重写的虚函数效果基类指针指向派生类对象(或基类引用绑定派生类对象)那么基类指针就可以调用派生类的重写(覆盖/override)的虚函数。这样你就可以用基类指针去使用别人定制的虚函数。如接口基类是你写的你可以用基类指针调用同事写的派生类具体功能虚函数Animal*animal1newDog();Animal*animal2newCat();animal1-makeSound();animal2-makeSound();5.动态多态的实现是怎样的原理和过程4.为什么析构函数建议是虚函数(虚析构函数)(1)如果派生类中有指针数据成员申请了堆空间则基类的析构函数必须设为虚函数。(2)不设为虚析构函数则delete pbase 时只调用基类的虚构函数而不会调用派生类的析构函数。造成派生类中指针申请的堆空间没有释放造成内存泄露。6.为什么构造函数不能是虚函数派生类在创建对象时派生类的构造函数的初始化列表中会先调用基类的构造函数去创建基类子对象。若基类的构造函数是虚函数则此处调用会被覆盖导致基类子对象无法创建也就无法创建派生类对象。派生类对象都创建不了就无法满足动态多态的5个条件无法实现动态多态了。7.基类和派生类的构造顺序、析构顺序先构造基类子对象再构造派生类对象。先析构派生类对象再析构基类子对象。//只创建一个派生类对象,观察基类和派生类的构造、析构顺序Derived d;Base()Derived()~Derived()~Base()6.STL1.vector、list、array的对比2.map、unordermap的使用区别、底层实现的区别7.设计模式 2种(1)单例模式(2)工厂模式8.现代C11/14/17(1)C11有哪些新特性请举例说明9.多线程10.网络编程11.拷贝构造函数的三种调用时机1.用已经存在的对象创建并初始化一个同类型的新对象Point pt2pt1;2.对象作为参数进行传递时实参初始化形参voidfunc(Point pt){}func(pt);3.函数返回时返回值是对象returnpt1;12.友元的三种形式1.将普通函数声明为友元函数2.将其他类的成员函数声明为友元函数 (繁琐不推荐)3.友元类13.运算符重载的三种形式1.普通函数实现但声明为友元函数2.普通函数实现调用公有接口以访问私有成员如get函数3.成员函数实现14.初始化顺序、赋值顺序1.初始化列表的初始化顺序数据成员的声明顺序2.参数的赋值顺序从右向左15.重载、隐藏、覆盖1.重载 (overload)同一作用域下函数名相同参数不同 (个数、类型、顺序任一不同即可)。如函数重载、运算符重载。2.隐藏 (oversee)发生在基类和派生类之间的数据成员和成员函数同名。【派生类的同名函数隐藏基类的同名函数。只要求函数名相同。数据成员也只要名字相同。】3.覆盖/重写 (override)发生在基类和派生类之间的虚函数。派生类的函数与基类的虚函数函数名相同、参数的个数和类型相同、返回值类型均相同则派生类可以覆盖基类的虚函数。覆盖的是虚函数表中虚函数的入口地址。【经覆盖指向派生类对象的基类指针可以调用派生类重写的虚函数。】16.构造函数1.类的构造函数与类的普通函数一样都需要携带隐藏的参数this指针2.派生类的构造函数会先调用基类的构造函数汇编层面会添加三行代码3.如果存在虚函数构造函数会用虚函数表的地址来初始化虚函数指针并保存在对象实例里面。17.函数指针与指针函数1.函数指针指向函数的指针intadd(inta,intb){returnab;}int(*func_ptr)(int,int);func_ptradd;intresultfunc_ptr(2,3);2.指针函数返回值类型为指针类型的函数int*fuc(){intnum100;int*pnum;returnp;}二、Linux命令1.文件与目录操作ls, cd , pwd, mkdir, rm ,cp ,mv, find2.文件内容查看与编辑vim/nano, cat, head/tail, less/more3.文本处理与过滤grep, awk, sed, wc4.系统与进程管理ps, top/htop, kill, df, du, free5.权限与所有权chmod, chown, sudo6.网络与连接ping, curl/wget, ssh, scp, netstat7.压缩与归档tar, zip/unzip问题1如何查找当前目录及其子目录下的所有扩展名为.log的文件答案find . -name “*.log”问题2(1)有一个名为blt.log的文件如何快速统计出总共有多少行(2)如何在终端只看最前面10行最后面10行呢答案(1)①用vim G划到底部set :nu可以看到行数②用wc -l blt.log(2)head -10 blt.logtail -10 blt.log问题3如何从burnin.log中过滤出所有包含[error]关键词的行并将这些错误行保存到另一个文件error.log中答案grep “[error]” burnin.log error.log (考察grep和重定向)问题4如何压缩和解压缩答案zip/unziptar -czvf/ tar -xzvf问题5如何修改一个文件的权限仅仅只给文件blt.py增加执行权限怎么做答案chmod x blt.py问题6如何查看一个目录下各子目录和文件的大小答案du -sh *附加问题了解哪些内存外存监控命令1.监控内存情况-h 是以人类可读方式将默认的KB单位换为MB或GB(1)freefree -h 查看内存使用情况总内存、已使用内存、空闲内存(2)top查看各项资源使用情况类似Windows的任务管理器(3)dudu -h 查看目录所占磁盘空间①du -sh显示指定目录的总和不递归显示每个文件或子目录的大小。②du -ah查看所有文件和目录所占磁盘空间(4)df查看文件系统(分区)的磁盘使用情况问题7ls后发现当前目录下都是白色如何显示颜色答案1本次命令临时显示颜色ls --colorauto答案2长久保存显示颜色(1)vim ~/.bashrc(2)alias ls‘ls --colorauto’(3)source ~/.bashrc三、Git命令1.上传add、commit、push1.添加添加文件到暂存区gitaddfile-name#添加单个文件gitadd.#添加所有更改的文件2.提交提交文件到本地仓库gitcommit-m提交信息(姓名: 操作内容)3.推送把操作推送到远程仓库gitpush origin master#gitee的默认分支名为mastergitpush origin main#github的默认分支名为main2.下拉git pullgitpull originbranch-name#从远程分支拉取最新代码并合并到本地gitfetch originbranch-name#仅下载远程分支的最新更新,但不合并(后续自己手动合并)3.查看分支git branchgitbranch#查看本地分支gitbranch-v#查看分支的最近提交信息gitbranch-a#查看所有分支(包括远程分支)gitbranch-r#查看远程分支gitstatus#查看当前分支的详细信息gitbranchbranch-name#创建分支gitbranch-dbranch-name#删除本地分支gitpush origin--deletebranch-name#删除远程分支4.下载仓库gitclone5.查看分支答git branch -av6.切换分支答git checkout 分支名7.更新子仓库答git submodule update --init –recursive四、C构建工具链(Build Toolchain)、构建命令1.如何查看camke的版本答cmake --version2.cmakeninja构建命令答cmake -S 源码路径 -B build -G Ninja五、Python和Shell1.用Python实现一个函数六、操作系统1.32位系统的内存布局(1)内核态3-4G高地址(2)用户态0-3G低地址①栈区操作系统分配的内存②堆区程序员手动申请的内存malloc、new③全局/静态区存全局变量、静态变量④文字常量区存常量⑤程序代码区存代码链接C内存布局2.死锁1.死锁的概念两个或多个线程或进程在执行过程中为争夺资源而造成的僵局。2.死锁发生的4个必要条件①互斥②不剥夺③请求与保持④循环等待3.死锁的3种处理①死锁预防破坏死锁发生的四个必要条件之一②死锁避免银行家算法③死锁的检测与解除资源分配图3.用户态和内核态、宏内核与微内核1.用户态与内核态内核态可以执行特权指令。用户态只能执行用户指令。特权指令I/O指令、系统调用、开/关中断指令、修改PSW2.宏内核与微内核①宏内核是指内核空间包含了大多数的操作系统服务如进程管理、内存管理、文件系统、设备驱动等。这样切换服务时上下文切换成本低开销小性能高。缺点是不稳定、不安全一个服务崩溃可能导致整个内核崩溃。②微内核的设计理念是尽可能地将系统服务和系统核心功能分离微内核系统中只包含最基础的服务如低级的地址空间管理、进程调度和通信机制。优点是安全稳定一个服务故障不会影响到内核。缺点是切换服务上下文开销大性能低。七、网络编程 / 计算机网络1.TCP、UDP区别2.Socket通信过程3.三次握手1.三次握手的过程①第一次握手客户端发送SYN报文 (SYN1seqx)然后进入SYN-SENT状态②第二次握手服务器先是处于LISTEN状态收到SYN报文后回复SYN ACK报文(SYN1ACK1seqyackx1)然后进入SYN-RCVD状态③第三次握手客户端回复ACK报文(ACK1,seqx1acky1)然后进入ESTABLISHED状态。服务器收到后也进入ESTABLISHED状态。2.为什么要三次握手两次握手行不行四次握手行不行(1)两次握手若没有第三次握手相当于服务器没有SYN-RCVD状态直接进入ESTABLISHED状态会存在问题如果没有第三次握手若客户端发送的SYN报文在网络中延迟并在客户端的超时时间内未收到服务器的ACK则客户端会重新发送SYN报文。没有第三次握手进行确认而只有两次握手服务器就会错误地认为这是一个新的连接请求回复的ACK也会被客户端抛弃。会占用服务器一个端口而不发送任何数据造成资源浪费。(2)四次握手三次就够了四次是多余的。三次握手已经足够确保双方通信的可靠性和同步性第一次握手客户端通知服务器“我想建立连接”。第二次握手服务器通知客户端“我收到了你的请求并准备好建立连接”。第三次握手客户端通知服务器“我收到了你的确认现在我们可以通信了”。3.三次握手的过程图4.SYN泛洪攻击 (SYN Flood 攻击)客户端只发送一次握手不进行第三次握手。大量客户端发送SYN报文使得服务器接受后就处于等待ACK的SYN-RCVD状态。此时不回复ACK就会让服务器一直处于该状态除非服务器主动断开连接。大量占用服务器直至崩溃。4.四次挥手1.过程客户端与服务器刚开始都处于ESTABLISHED状态。①第一次挥手客户端发送FIN报文(FIN1sequ)然后进入FIN-WAIT-1状态②第二次挥手服务器接受到FIN报文并回复ACK报文(ACK1seqvacku1)然后进入CLOSE-WAIT状态③第三次挥手服务器发送FIN报文(FIN1,ACK1,seqw,acku1)然后进入LAST-ACK状态④第四次挥手客户端发送ACK报文(ACK1,sequ1,ackw1)然后进入TIME-WAIT状态若2MSL内未收到服务器重发的第三次挥手则CLOSED。服务器若成功收到第四次挥手则CLOSED。2.过程图3.第一次、第二次挥手关闭客户端的写操作、服务器的读操作。第三次、第四次挥手关闭服务器的写操作、客户端的读操作。八、数据库1.数据库的事务和索引九、Qt1.Qt信号与槽机制2.Qt元对象3.Qt 事件机制 或者 事件循环4.GDB调试、你一般都用QtCreator调试嘛十、手撕算法1.二分查找2.LRU Cache3.滑动窗口4.排序(1)手撕快速排序原理筛选一个基准元素将待排序序列分割成两个子序列。使左边子序列所有元素都小于等于基准元素右边子序列所有元素都大于等于基准元素。然后再对两个子序列分别进行快速排序直到整个序列有序。若交换另一个while(执行一次)负责移动下标未交换的时候本while负责移动下标核心代码intPartition(intA[],intL,intR){intmidA[L];while(LR){while(A[R]midLR)R--;A[L]A[R];while(A[L]midLR)L;A[R]A[L];}A[L]mid;returnL;}voidQuickSort(intA[],intL,intR){if(LR)return;intMPartition(A,L,R);QuickSort(A,L,M-1);QuickSort(A,M1,R);}对5,3,2,6,7,9,1,4,8,0进行一轮快速排序得到的序列应该是0,3,2,4,1,5,9,7,8,6完整代码#includeiostreamusingnamespacestd;intPartition(intA[],intL,intR){intmidA[L];while(LR){while(A[R]midLR)R--;A[L]A[R];while(A[L]midLR)L;A[R]A[L];}A[L]mid;returnL;}voidQuickSort(intA[],intL,intR){if(LR)return;intMPartition(A,L,R);QuickSort(A,L,M-1);QuickSort(A,M1,R);}intmain(){intA[10]{5,3,2,6,7,9,1,4,8,0};//排序前for(inti0;i10;i)coutA[i] ;coutendl;//快排QuickSort(A,0,9);//排序后for(inti0;i10;i)coutA[i] ;coutendl;return0;}(2)冒泡降序排序2.降序排序小值从前往后冒泡voidBubble_des(intA[],intn){for(inti0;in-1;i){for(intj0;jn-1-i;j){//从前往后if(A[j]A[j1]){//对于降序条件改为小于inttempA[j];A[j]A[j1];A[j1]temp;}}}}4.降序排序大值从后往前冒泡voidBubbleSort_des(intA[],intn){for(inti0;in-1;i){for(intjn-1;ji;--j){//从后往前if(A[j]A[j-1]){//对于降序,改为大于inttempA[j];A[j]A[j-1];A[j-1]temp;}}}}5.四维图新面试算法题算法题(1)视觉算法相关题目①NMS算法②均值滤波③YOLO中reorg算子实现④FocallLoss⑤匈牙利匹配算法(2)数据结构①链表节点插入②二叉树遍历前中后序层序③二叉搜索树 BST④哈希表⑤岛屿问题(dfs,bfs)岛屿的数量(3)通用算法①LRU Cache②合法括号匹配③滑动窗口④二分查找⑤快速幂⑥重复元素(哈希表)⑦合并两个链表⑧前k个高频元素 (Top K)(4)编程能力①string to float②矩阵旋转③内存拷贝④最长回文子串⑤生产者消费者模型 (C实现一个生产者消费者的多线程模型)⑥线程池 (C实现)⑦内存池 (C实现)