当前位置:网站首页>454-百度面经1
454-百度面经1
2022-07-06 17:59:00 【liufeng2023】
0、项目
项目:
- 代码传到github上去!
- 不要过多的去描述项目的业务!
- 描述项目的设计,重写的开源库,有什么样的好处!高并发,缓存,解耦网络层、业务层、db层的强耦合!用到了微服务、消息队列,redis就是将业务层和db层解耦了!
介绍项目:
- 项目背景;为什么做!(聊天服务器,想根据某个业务场景,设计一个高并发的业务的聊天服务器!)写完服务器测试一下性能,支持同时在线的10000人的在线聊天!再提升,怎么做,加集群!还有种方式将它的服务拆分成非常小的服务,做成基于分布式的架构,基于微服务的模型!
- 聊天服务器项目的架构设计,网络层、服务层、数据传输采用的是Json协议,网络使用的是muduo库,基于epoll+多线程的高并发模型!
面试官说:你写的项目没有必要啊?
- 介绍为什么写这个项目!
- 每一次写一个项目都有收获!把前面…知识在项目上得到了实践锻炼,真正的了解了多线程开发,线程通信,在项目中得应用,和之前学习知识点后做的demo是完全不一样的;智能指针…真正的感受到了他的好处!
项目中遇到了哪些问题?
- 设计的问题,分析性能原因,引入了redis,也是问题!
- 碰见死锁!
- 碰见段错误,挂掉了!
- 测试服务器的瓶颈的时候,fd的上限的设置,怎么解决的?
- 项目的稳定性,安全性!
1、如何保证发送数据不丢且不重(项目相关)
IT技能的每一个描述点,能从项目的什么地方引入进去!
项目如何保证一件事情是否完成?三种情况;
- 协议本身帮你做了;
- 基于协议实现的第三方框架帮你做了;
- 自己写的应用,自己做的!
项目中怎样实现高并发的?
- muduo库本身就已经实现了高并发了,将muduo库中的
基于Rector模型IO复用 + 线程池
说一下
TCP保证发送数据不丢且不重?
- 协议本身就保证了;
- 不丢:超时重传机制;
- 不重:序号和确认应答机制;
应用保证发送数据不丢且不重?
- 也是类似于TCP的重传机制、序号和确认应答机制;
- 将TCP在传输层协议上的一个机制,在应用层上再来一遍!
面试的时候聊到TCP协议和UDP协议,面试官经常给你一种场景让你设计;
- TCP基于连接的,是流式协议;UDP是无连接的,是用户数据包协议,通信双方不管能不能收到,发送方只管发,接收方尽量收!
- TCP有安全的保障机制,超时重传、乱序重排、滑动窗口、流量控制、拥塞控制!在协议上保证数据可靠性传输!UDP不可靠的!
- TCP的ACK响应也是会占用网络带宽的!(现在带宽很好,光纤入户,可以不考虑! )
- 现在设计服务器类的程序,都是采用TCP协议!
- TCP虽然耗费了一点点的多余的流量,但是换来了非常可靠的传输!UDP不行!
- 服务器一定首先要保证高可用和高可靠!
TCP的三次握手和四次挥手!
2、线程池的线程为什么要动态扩容? & 进程池(nginx)/线程池(muduo)/内存池/连接池(数据库)
2.1、进程池(nginx)/线程池(muduo)/内存池/连接池(数据库)
- 不用池的话开销太大了,临时生成以及销毁代价都大!使用池;
- 需要用资源,从池里面拿,用完了也不释放,再返回池中;
- 进程和线程,在Linux环境下,创建进程线程时,底层用的也是克隆,内核中用哪个的还是fork!
- 项目中碰到小块内存的频繁开辟和释放时,malloc和free的效率是比较低的!使用内存池来提高小块内存的使用效率!
- 连接池也是一样,不断的TCP三次握手和四次挥手,效率降低,资源消耗大!
2.2、线程池的线程为什么要动态扩容?
不进行动态扩容的话,线程池就是死的,数量固定好不好?客观的看!
不进行动态扩容:
- muduo库的线程池没有动态扩容!采用Reactor模型 + epoll + 线程池,线程池中线程数量:IO数量 + 工作线程数量 和CPU的核数相同,这样才能最大利用CPU进行并行的处理!
进行动态扩容:
- 所有的池都是有一个初始的数量的!
- 如果所有的线程都被占用,此时由耗时的任务,需要扩容产生新的线程!
- 线程的上下文切换开销很大;所以要线程池;业务并发量无法预估的情况下;可以保证系统资源消耗小,性能得到保证;
- 当在一定时间内,有部分线程一直处于空闲状态的话,就将多余的线程释放掉!
线程开销:
- 32位linux下,4G虚拟内存, 3G用户, 每个进程能pthread_create 能创建380左右个线程 ,3G / 8M = 380+(8M为线程栈的大小)
- 线程的创建开销和进程的创建开销一样,都是很大的;
- 线程太多,线程本身占用的内存比较大,业务可以使用的内存就少了;
- 使用
ulimit -s
查看线程栈的大小,1000-2000线程的话,内存会被占一半了! - 线程的上下文切换;
- 造成服务器锯齿状负载;(1000-2000线程在阻塞等待时,当有事件到达时,那一瞬间,系统的cpu和内存消耗是非常大的,很容易造成服务器崩溃!所有我们不允许创建很多线程!)
3、假设与数据库交互出现问题,线程在和数据库交互时产生阻塞,线程不能继续执行了,会产生什么问题?
- 聊天服务器采用的是Reactor模型,一个IO线程复杂监听新连接,剩下的线程属于工作线程!一个线程在epoll上监听了很多个客户端的套接字,接收到一个用户的请求之后,就会开始做业务!做业务的过程中会涉及到数据库的curd;
- 如果一个工作线程阻塞了,导致注册在这个工作线程上的epoll上的所有的套接字后面发生的读写事件,当前线程无法处理了!
4、3中的问题怎么解决?
gdb调试,大秦坑王,有专门的调试文章!
解决方法:
- 使用连接池,当经过一定时间(设置为1-2s)无响应,会返回错误!这样和数据库交互的时候就不会产生阻塞!
- 工作线程不阻塞,工作这个线程上的其他clientfd就可以处理事件了!
5、C++中引用和指针的概念和区别?
从swap函数引入,形参想要改变实参的值,使用值传递不行;
C语言中使用指针,C++中使用引用!
在windows直接断点调试,发现指针和引用的汇编代码是完全一样的!
在Linux下,直接-g,使用gdb调试,disassemble查看反汇编代码!
在查看汇编指令的时候,定义一个引用,实际上也是要在栈上,
开辟4个字节的内存,将它要引用的变量内存的地址放到4个字节中;
通过引用变量来引用它所指向的变量的时候,实际上汇编上也是从之前底层开辟的那4个字节的内存的地址,自动做一个解引用的操作!
当通过指针解引用的时候,实际上是从指针的 4个字节取出指向的内存的地址,去访问所指向的内存;
可以看出在汇编的层面上是100%的相似! 即汇编层面是一样的,在语言层面是不同的!
C++中开发时尽量使用引用:
- 实际上引用起来和指针一样,但是引用必须初始化,所有引用相对指针安全一些;
- 但是指针也不能说不好,C和C++最大的一个特点,通过指针可以在内存上漫游,通过指针的++和–可以在内存上偏移;引用不能偏移,只要使用引用,就会自动偏移了!
- 指针给我们的扩展性较大,没有解引用指针,指针可以p+1,p+2进行偏移!
- 引用没有办法做内存上的偏移,直接进行的解引用了!
6、地址传递和引用传递区别?
7、实际应用中传递一个很大的数组,不想去改变它,但是还不想让他拷贝,怎么做?
挖坑题!
- 传递数组根本不会发生拷贝,传递数组名永远传递的是数组起始地址,传递的是指针;
- 因为只传递了数组的地址,我们在使用的时候,需要传入数组的长度传入!
- 直接传入数组的地址,就像当于指针传递,在函数的内部是可以修改数组的内容;如果不想修改,在数组参数前面加上const!可以读取数组内容,不可以修改数组内容!
- 再延伸,数组=》stl容器!stl容器就是我们的准备好的!
vecto容器和数组的区别:
- 容器不用自己管理内存,不够了可以扩容…
- 当传递一个容器的时候,实参到形参是一个拷贝构造的过程!如果函数中传递一个容器,性能就会大大下降,有拷贝构造的过程;这时应该通过引用进行传递!引用也是可以修改容器的,因此需要通过const常引用!
8、const A * p 和 A* const p的区别?
const修饰的是指针本身还是指针指向;
const A * p
:
- const修饰p,表示 p不能被赋值,p可以被赋值;
A* const p
:
- const修饰的是p,表示p不可以被赋值,*p可以被赋值;
仅仅是上面这样简单的回答吗?
怎么发散?
- 从实践的角度进行发散,表示我们用过!
实践角度进行发散:
const A * p
主要用在调用函数时, 实参是通过指针传递给形参,在形参中,我们只想访问实参,不想修改实参!所以形参的指针,在最前面加上const!可以读* p。不可以修改* p;A* const p
,即和this指针相关,p不可以被修改,不能给this指针赋值,this指针永远指向调用方法的对象的!可以通过this指针可以修改指向对象的值(*p可以赋值),但是不可以修改this的指向(p不可以修改)!
const修饰的额值都是常量,常量都是不能被修改吗?const int a = 10
如果直接a = 20,这是绝对不可以的!
怎么改?
int *p = (int *) &a;
*p = 20;
通过汇编修改:
这样就已经被修改了!
为什么又可以修改了?
- const修饰的变量不能被修改只是在编译阶段,语法级别上;
- 实际上,在汇编上,并不是说const直接将内存的属性给改了,从原来的可读可写变为只读不写,不可能的!
- const只是立足于语法层面,编译阶段,编译器能够检测出对一个const修饰的常量赋值了!但是经过编译以后,就到了汇编指令上,这个时候就没有const了, const只是在语法层面不能修改!
- C语言中,const是常变量;C++语言中,const是常量!
9、成员函数后加const是什么意思?会有什么样的限制?
从实践的角度去回答:
- 定义一个const常对象去调用普通方法时,我发现是调用不了的!
为什么调用不了?
- C++用一个对象调用一个方法,汇编之后,实际的调用过程还是一个C函数的调用!只是 将调用对象的地址当成实参传递给函数;所有在成员方法编译的时候,都会加一个形参this来接收对象的地址,但是this是一个指向非const对象的const指针,不能接收const对象;
所有当常对象调用成员方法的时候,成员方法的后面需要加const,影响了this指针,让this指变成指向const对象的const指针,这样形参this就可以接收对象了!常对象就可以调用常成员方法了!
限制:
- 在常方法中,只能访问对象的成员变量,不可以改变!
- mutable修饰的变量是依旧可以修改的!
this指针
- 每个对象拥有一个this指针,通过this指针来访问自己的地址。
- this指针并不是对象的一部分,this指针所占的内存大小是不会反应在sizeof操作符上的。
- this指针只能在成员函数中使用,全局函数、静态函数都不能使用this指针;
- 在普通成员函数中,this是一个指向非const对象的const指针(如类类型为Student,那么this就是Student *const类型的指针);
- 在const成员函数中,this指针是一个指向const对象的const指针(如类类型为Student,那么this就是const Student * const类型的指针)
- this指针在成员函数开始执行前构造,在成员函数执行结束后销毁。
const对象与const成员函数
- 在成员函数参数列表后面加上const修饰,表示函数内this指针是一个指向常量对象的常量指针,不能修改成员变量。
- 一个const成员函数如果以引用的形式返回*this,那么它的返回类型是常量引用;
- 对于const对象或者const对象的引用和指针,对象内的成员变量是不能修改的,因此只能调用const成员函数,不会修改成员变量;
- 对于非const对象,既可以调用const成员函数,也可以调用非const成员函数。
常量成员函数特点
- 常量成员函数不能改变调用它的对象的内容。
- 常量对象,以及常量对象的引用或指针都只能调用常量成员函数。
- 不能在一个常量对象上调用普通成员函数。
- 如果常量成员函数在类外部定义,也必须在参数列表后面加上const关键字。
- const成员函数中可以修改添加了mutable关键字的成员变量的值。
- 静态成员函数与对象无关,因此不能被声明为const。
10、类中const成员如何初始化?
定义必须初始化的成员变量,必须放在初始化列表中(const变量、引用成员变量)
11、vector如何管理内存?
从vector的底层说起,它是一个动态扩容的数组;
容器依赖空间配置器allocator来进行管理内存,空间配置器中有4个方法非常重要!
vector只负责对象的构造、析构,内存管理都是靠allocator空间配置器来管理的。
在Linux下是
0 - 1 - 2 - 4 - 8
2倍扩容的!vs2017,2019下是1.5倍扩容;(从实践的角度,编写代码,不断的插入元素,每次打印vector的容量以及实际大小,探究扩容机制。capacity返回vector的容量,sizeof是返回vector中实际元素的大小)小结下,这就是vector如何管理内存的;
话锋一转!还可以介绍一下vector的其他重要的方法;(vector还有很多其他有用的方法,比如说…)
所有C++ STL容器在进行管理底层对象内存的时候,不可能直接用new和delete,为什么不能用,上课的时候说过!
所有容器都必须依赖allocator空间配置器,里面有4个方法:construct、destory、allocate、deallocate;
construct、destory负责对象的构造和析构;allocate、deallocate负责内存的开辟和释放,就是在vector管理内存的时候,一定要将对象new的对象的构造和内存开辟分隔开,将delete的对象的析构和内存的释放分隔开!(原因:初始化一个vector的时候,如果用new的话,不仅仅会开辟一个内存,还会构造一些没有用的对象,不符合我们的逻辑,所有底层的内存开辟是靠allocate来的,allocate和deallocate都是用的默认C库的malloc和free)
如果应用层使用vector比较频繁的话,小块内存使用比较频繁,可以使用内存池来替换allocate和deallocate中使用的malloc和free;
再扩展,在C++11以后提供的带右值引用的拷贝构造和赋值重载!很大程度的提高了vector拷贝和构造的性能!
12、vector什么时候会发生迭代器失效?(从编程习惯考虑如何避免)
- 在自己实践的过程中,有没有接触过迭代器的失效?
- 当我们用vector解决问题的时候,有可能实际到遍历vector,即读取时,迭代器不会发生失效;
- 在进行insert和 erase的时候,迭代器就会失效,insert和erase中接收的都是迭代器,不仅仅是vector,相关的容器的方法,一般接收的都是迭代器,我后面再说!
- 迭代器的底层实际上是对一个指针的封装,拿vector来说,vector的迭代器的底层是一个指针,vector的迭代器的指针指向的是vector底层的一个数组;
- 迭代器通过该运算符重载,以用相同的方式 遍历不同的容器! 因为不同的容器的遍历方式都被封装在迭代器的++,- - 运算符中;
- insert可能回到值vector容器的扩容,内存位置都变了,迭代器的底层是一个指针,指向一个地址,扩容之后,迭代器肯定就失效了!
- 怎么处理?insert和erase返回值会返回新位置的迭代器,需要将迭代器的循环变量更新下;
- erase删除元素之后,后面所有的元素都有向前挪,erase从首元素到当前删除元素之间的迭代器还是有效的!但是当前位置到末尾元素迭代器都会发生变化,需要更新!
现在开始扩展:
- 迭代器对于容器时非常重要的!
- 每一种容器都有字节的迭代器,因为每一种容器的底层数据结构都是不一样的;
- 但是实际上写代码的时候,发现迭代器遍历容器的代码都是一模一样的,这是怎么做到的?因为迭代器提供++、- -运算符重载函数,虽然不同容器,不同数据结构元素的迭代方式不一样,但是不同数据结构的不同迭代方式都被封装在迭代器++、- -运算符重载函数中,所以用迭代器操纵不同容器,代码是一模一样的!
- 不仅容器方法接收的增加删除用的迭代器,泛型算法用的也是迭代器!
13、用拷贝构造给一个复杂vector赋值不太好,如何解决?
- 意思就是vector里面装的是大对象,vector的拷贝构造会造成很多大对象的拷贝构造;
- 赋值会先临时构造右边的对象,根据右边的资源,给左边的对象开辟内存,再给它的内存上构造很多对象,再将右边临时对象析构,将内存析构掉!效率是很低的!
- 视情况采用std::move调用vector的右值引用拷贝构造和赋值重载(右值引用又称为移动语义,不会按照右边的尺寸给左边开辟内存,构造对象,而是直接将右边的资源全部拿过来!)
- vector除了拷贝构造和赋值,其他方法也都提供了带右值引用的方法!
14、map/multimap区别
将关联容器 + 底层数据结构 + 常用的方法融合到一块!
map/multimap的底层都是红黑树,说完这个可以说数据结构的的哈希表和红黑树!
延伸到哈希表,可以说哈希冲突,解决方案!
说到红黑树,可以将BST和AVL都说一下,这些都属于二叉搜索树,只是各自有各自的特点!
如果只说 map的key是可以重复的,multimap的key是不可以重复读的是不行的!
回答问题尽量从实践的角度出发!
思路:
- 什么时候会用到map,做什么的时候会用到map;从实践的角度切到map;
- 然后再切到map/multimap的区别上!
- 实践 + 理论!
map和multimap都称为映射表,主要处理数据有关联关系的键值对的场景! 比如说用一个学生的学号对应一个学生成绩,通过快速查找学号来得到学生的数据!
map和multimap主要的区别是key值是否能重复!value是可以重复的!
接着介绍底层的数据结构红黑树 !所以map增删查的时间复杂度都是O(log2n),效率非常快的!
红黑树一般运用在对于key有排序性要求的场景下!
再延伸,就说,实际上大部分场景对key的有序性是没有要求的,我一般在看一些开源库的时候,muduo网络库的时候,都是用的是unordered_map 和 unordered_set!点到为止!
再延伸,Linux的虚拟内存管理用的就是红黑树,效率是非常高的!早期的Linux的内存管理用的是AVL,最后Linux3.6版本之后又改成了红黑树!因为红黑树相对于AVL树,它的优势是…
这样我们就延伸到了AVL树,是一个平衡树,它是一个基于内存的!
接着说,还有基于IO操作的平衡树,B树和B+树…这是数据库索引常用的,它的好处是…
问题的核心详细说出来,相关联的内容点到为止,等待面试官提问!
顺序容器vector、list、deque
- 尤其是vector和list,映射与基础数据的数组和链表;
- 说到容器,将其迭代器、泛型算法都说说!
15、智能指针
真正工作时,使用裸指针的情况是非常少的!
说起它的时候,还是要从实践的角度说起,要他有什么用?
- 目的: 自动释放资源(对象资源,文件资源),就是担心有些资源用完了没有被释放,就会产生资源泄漏!
- 智能指针利用栈上对象出作用域自动析构特点,将资源的释放代码写到智能指针的析构函数中!
面试时这样介绍:
- 在C语言中,动态分配堆内存用的是malloc+free;在C++中用的是new+delete;
- 但是手动开辟,可能会 free和delete忘记写了,或者其他文件资源忘记关闭了!
- 为了自动解决这些问题,使用智能指针,利用的栈上对象出作用域自动析构特点;
智能指针从大的方向上分有两类,不带引用计数的和带引用计数的!
- 不带引用计数:C++标准库之前提供的auto_ptr,介绍一下;auto_ptr问题:会自动交出管理对象的所有权!所以auto_ptr在拷贝构造和赋值之后,原来的auto_ptr就不能再使用了,这是坑,他没有任何防范措施,理论上是可以使用的,但是一旦使用就会出错!auto_ptr不能用在容器中,容器的拷贝构造会涉及到每一个元素的拷贝构造和赋值,最后还是涉及到auto_ptr的拷贝构造和赋值!
- 不带引用计数的还有unique_ptr,我们推荐的,unique_ptr去掉了左值的拷贝构造和赋值重载,公布出了右值的拷贝构造和赋值重载,好处是…
- 带引用计数:如果想要多个智能指针管理一个资源,使用引用计数,shared_ptr,底层使用CAS操作来保证引用计数加减的原子操作,可以直接使用在多线程环境下!
经典问题:有了shared_ptr为什么还要weak_ptr,强弱智能指针?
- 从实践出发,如果只使用shared_ptr会出现循环引用问题,介绍自己实践的例子,怎么解决?
还介绍了shared_ptr和weak_ptr在解决多线程访问共享C++对象的线程安全的问题(课上讲的)
还有enable_shared_from_this和shared_from_this获取当前对象的一个智能指针;
- shared_from_this:在当前对象的成员函数中如何获取当前对象的一个shared_ptr指针;
- 不能通过当前对象的this指针直接穿件一个shared_ptr,会导致两个shared_ptr都认为只有自己持有了这个资源,最后释放资源的时候,会释放2次,出现问题!(大秦坑王blog)
继续延伸:make_shared
作用:为shared_ptr填坑;
shared_ptr的操作分为2步:
- 构造shared_ptr会给它传入一个new的内存,shared_ptr底层还会new一个控制资源的控制块,也相当于一个内存块,上面记录了资源的地址 和资源的引用计数;
- 现在假设管理的外部资源new成功了,底层的控制块new失败了,怎么办?存shared_ptr本身就没有创建起来,最后资源就不会帮我们自动释放,智能指针本身的创建有问题;
- C++11以后给我们提供了make_shared,获取一个指向资源的shared_ptr,非常安全的!
C++14以后提供了make_unique,补不带引用计数的unique_ptr的坑的!
16、设计:10万个正整数,取值范围0到100w,互不重复,给定一个数字判断是否在这些数中出现过?
面试参考的问题:
- 1、大数据求topK的问题;
- 2、查重的问题;(真没有思路,哈希表!增删查的时间复杂度都是O(1))
哈希表的缺点:
- 空间换时间,占用内存大!
- 常见的链式哈希表,发生哈希冲突的元素放在一个桶中,一个节点表示一个元素,链表串起来,一个节点包含 该节点元素 + 下一个节点的地址!假设存储的是int类型,
10万个元素就需要40万个字节 + 下一个节点的地址40万个字节
,总共80万个子节;
查重优化的话:
1、位图法;
- 一个位记录一个数字,1个字节8个位,1个字节可以记录8个数字;
- 位图法非常节省内存,只能判断数字是否出现过;
- 是用数组来记录的,必须先知道,10万个整数最大的位是多少;需要根据最大的数组来定义位图数组的大小!
2、布隆过滤器;
- 存在一定误判;
- 布隆过滤器说 数字不在,那么数字一定不在;布隆过滤器说数字在,数字不一定在;
- 我们需要提前说明,这个问题允不允许一定数量的误判!
- 布隆过滤器常用于redis的缓存中,比如在缓存中,命中key的话,允许一定的误判,提高身体缓存的效率!
17、两个有序数组取交集?代码层面优化?n个数组取交集?
1、哈希表;
- 一个数组元素先构建哈希表,另外一个数组的元素在这个哈希表中查;
- 问题:题目中的有序数组没有用到,不是最优的!
- 时间复杂度: O(m)xO(1)
2、二分查找;
- 遍历第一个数组的每一个元素,在第二个数组中进行二分查找;
- 时间复杂度: O(mlogn)
- 问题:还是没有将两个数组都有序全部用上,只用了一个数组是有序的!
3、双指针;
- 时间复杂度: O(m) + O(n) = O(m + n)
总结对比:
- 两个数组的个数相近(使用双指针); 比如都是10000个,二分查找时间O(10000*13)= O(130000),双指针O(20000);
- 两个数组的个数相差很大(使用二分法); 一个10个,一个20000个;双指针O(10 + 20000),二分查找O(14*10);
n个数组取交集?
- 1、划分成两两数组,一直做下去!
- 2、n个指针进行处理!
边栏推荐
- Byte P7 professional level explanation: common tools and test methods for interface testing, Freeman
- C语言实例_4
- Neon Optimization: an optimization case of log10 function
- Transformation transformation operator
- golang中的Mutex原理解析
- How to manage distributed teams?
- LeetCode. 剑指offer 62. 圆圈中最后剩下的数
- 交叉验证如何防止过拟合
- Google released a security update to fix 0 days that have been used in chrome
- Taro2.* applet configuration sharing wechat circle of friends
猜你喜欢
go-zero微服务实战系列(九、极致优化秒杀性能)
Gazebo的安装&与ROS的连接
Clickhouse fields are grouped and aggregated, and SQL is queried according to the granularity of any time period
golang中的Mutex原理解析
2022 Google CTF SEGFAULT LABYRINTH wp
LeetCode. 剑指offer 62. 圆圈中最后剩下的数
How to manage distributed teams?
Your cache folder contains root-owned files, due to a bug in npm ERR! previous versions of npm which
HMM notes
tansig和logsig的差异,为什么BP喜欢用tansig
随机推荐
POJ 3177 Redundant Paths POJ 3352 Road Construction(双连接)
Yunna | work order management software, work order management software app
Machine learning: the difference between random gradient descent (SGD) and gradient descent (GD) and code implementation.
405 method not allowed appears when the third party jumps to the website
golang中的Mutex原理解析
C language instance_ four
NEON优化:log10函数的优化案例
Dark horse notes - exception handling
Google released a security update to fix 0 days that have been used in chrome
Boot - Prometheus push gateway use
Lldp compatible CDP function configuration
Spark TPCDS Data Gen
Force buckle 1037 Effective boomerang
Receive user input, height BMI, BMI detection small business entry case
1123. 最深叶节点的最近公共祖先
JTAG principle of arm bare board debugging
Implementation principle of waitgroup in golang
[advanced C language] 8 written questions of pointer
golang中的atomic,以及CAS操作
NEON优化:性能优化常见问题QA