struct和class的区别
指针和引用的区别?c++为什么提供了引用这个东西?
说const 指针和指针 const的区别?例如const A*是什么意思?了解const 函数吗?具体是不修改哪些数据成员呢?
多态。追问:动态多态里,怎么确认调用虚函数的对象要调用的是哪个函数?
虚函数表,通过指针的类别去查表,识别要调用的函数
什么场景下需要使用虚析构?
场景问题:有一个接口,是对那个文件进行操作,接口实现里有文件加锁和解锁操作。假设这个函数里面有很多的return,怎么能保证他return返回的时候成功解锁?
我们出函数的时候是什么东西是一定会被释放?利用这个局部变量的特性来处理。
简单说堆和栈的区别。局部变量在堆还是栈上?函数的参数呢?
vector假如调用erase()删除元素,元素都会移动,假如就是要在循环里删除元素会造成什么问题?如何解决问题顺利进行遍历?
HINT: erase()返回的是什么?利用erase()函数进行删除操作,它返回的是删除元素下一元素的iterator迭代器。
说一些高效使用vector的方法。怎么预留?
提到了emplace_back(),追问:它是在push_back()之后推出的,有什么优势?
说list链表和vector的区别。
怎么反转一个单向链表?递归的话代码具体怎么实现,需要保存几个指针?
答2个(面试官说3个当前的、下一个的,上一个的),我还是觉得是2个
怎么对一个二叉树求高?
括号匹配:一个表达式里面有很多括号我们怎么确认他这个括号左括号和右侧是匹配的压栈。扫描整个表达式,如果是左括号的话就先进行压栈,然后如果识别到右括号的话就弹出来栈顶元素进行匹配,不匹配的话就退出
怎么对四则运算求值(不考虑括号)。
考虑括号呢?
后缀表达式+栈
自旋锁和互斥锁。
有哪些场景会导致栈溢出?
了解几个字母编码吗?unicode之类的?
进程间通信有哪些方式?写过代码吗?
调试问题:假如有1000轮循环,我想进入第100轮调试,怎么进去?
4、写一个shape基类,rectangle和circle两个子类,计算他们面积。(写完)析构函数为 虚的?构造函数的调用顺序,构造函数有几种,分别什么作用
3.这段代码执行会发生什么
C++
#include<iostream>
class A{
public:
void f(){
}
};
int main(){
A* a=nullptr;
a->f();
return 0;
}
(一开始在我看来,这就是未定义行为,应该是指向内存空间中的保留区或者是任意的一个地址;之后我查了,是因为类的成员函数保存在代码段,然后被所有对象所共享吗?然后没有通过this指针去访问类中的相应成员变量,所以仍然是可以调用的嘛)
11.计算这个类的大小
#include<iostream>
class base1{
private:
int a;
char c;
public:
virtual void fun1(){}
virtual void fun2(){}
};
int main(){
std::cout<<(sizeof(base1));
return 0;
}
在C++中,类的sizeof结果不仅取决于类中声明的成员变量的大小,还受到编译器实现、对齐要求(alignment)以及虚函数表(vtable,如果存在虚函数的话)等因素的影响。
对于你给出的base1类,它包含两个私有成员变量:一个int类型的a和一个char类型的c。此外,它还声明了两个虚函数fun1和fun2。由于存在虚函数,编译器会为这个类生成一个虚函数表(vtable),用于在运行时确定调用哪个函数。
成员变量大小
int类型通常占用4个字节(这取决于编译器和平台,但4字节是常见的)。
char类型占用1个字节。
虚函数表(vtable)
虚函数表是一个指向函数指针数组的指针,它存储在类的实例中,用于在运行时解析虚函数的调用。vtable本身的大小和位置取决于编译器的实现,但它通常会增加类实例的大小。
对齐要求
编译器可能会根据平台的要求对成员变量进行对齐,以确保访问速度。这可能会增加类实例的总大小。
示例分析
对于base1类,即使它只有两个相对较小的成员变量,但由于存在虚函数,其sizeof结果通常会大于这两个成员变量的大小之和(即5字节)。实际上,由于对齐和vtable的存在,sizeof(base1)的结果可能会是8字节或更多,具体取决于编译器的实现和平台。
有一段实习是在软件所 plct实验室用模板元编程写simd库的,面试官好像不太感兴趣,项目是XV6加上自己的一些魔改,改进了一些系统调用啥的,感觉最后unique指针没写好,以及全程和面试官不在同一频道
14.面试官准备代码,判断执行顺序;创建vector容器,然后进行扩容,resize和reserve,插入元素push_back和emplace_back;判断构造函数、拷贝构造函数、移动构造函数的调用,以及noexcept、move关键字使用,多次扩容之后又是怎样?
15.Map和unordered_map的区别和实现原理,以及查找时间复杂度;
2.muduo网络库底层处理I/O连接用到的接口
(不知道他想问我epoll还是muduo中的那些具体组件,我回答了epoll)
附muduo库的底层组件:
EventLoop:事件循环,负责事件的分发和调度。
Channel:通道,负责事件的注册、响应和分发。
Poller:多路复用器,用于监听文件描述符上的事件。
Socket:套接字操作相关的接口,包括创建、绑定、连接等。
Acceptor:接受器,用于接受新的客户端连接。
TcpConnection:TCP连接对象,表示一个TCP连接,并提供相关的操作接口。
9.算法题:不断输入字符直至终止符(用getchar ()!= EOF,因为要求空格也算字符),将这些字符去重并逆序数出。
题目很简单,但面试官要求最低的空间开辟和时间消耗。我是用string+unorderd_map做的,面试官给出条件ASCII码代表128个字符,问我unorderd_map开辟的内存大小是多少(不知道),该怎么优化(开一个128个容量的vector?)
在继承情况下构造函数及析构函数的执行顺序
在不考虑继承的情况下,一个类的数据成员的初始化顺序
C++运行时多态的原理。
为什么会将一个类的析构函数声明为虚函数?
final,基类的虚析构函数,什么时候需要提供拷贝构造函数,vector的底层原理,申请内存的方式,堆栈的区别;
进程之间如何进行通信,管道的底层原理;监控进程的方法,线程安全;
图形学的相关知识,框选一个矩形,如何判断与这个矩形相交的其他矩形;除了暴力查找,还有什么什么更快速的方式(提示往数据结构方面考虑);知道哪些树,avl树。
多线程编程以及其他提高程序运行的方法。答了GPU加速,Cuda库的使用
2. 解释,vector中的in-place和push_back
in-place(原地构造)和push_back(插入新元素),是在容器内直接构造元素,而不是线构建临时对象然后在拷贝或移动到容器中。 这样可以避免不必要的拷贝和移动,提高性能。 使用emplace_back实现。
push_back:会将元素添加到vector的末尾,而insert可以在指定位置插入元素。
insert 函数可以在指定位置上插入元素。
1、C语言的编译过程:预处理、编译、汇编、链接
- 线程的通信方式:共享内存、消息传递和管道流
5、进程的通信方式?
一个类里面被const修饰的成员函数,可以访问哪些数据?可以访问const的成员变量吗?可以访问别的const修饰的成员函数吗?
MySQL为什么用B+树?B+树的树高比较矮,有什么好处?B+树的查询效率真的比红黑树高吗?
说说C++程序在运行的时候的内存布局?vector里面存储的元素会被放在哪块地方(堆?栈?)
进程间通信的方式有哪些?共享内存用过吗?
说说动态链接库和静态链接库的区别?(只回答出dll可以减小exe的大小,lib会包含到exe里面)
你知道recv()接口在对端已经关闭的情况下会返回什么错误吗?
平时用过gdb吗?程序一般在哪些情况下会发生core dump?(说了两个 数组越界访问、野指针 空悬指针, 算术计算 除以 0 这个没说出来)
8.close wait和time wait它俩的区别在哪里?
9.那像这个去优化它的参数,调整内核的这个参数,你大概能说出来几个吗?就是针对time_wait规则优化这一块,内核参数。
1、select、epoll、poll的实现机制,为什么epoll更快
单线程+IO多路复用和多线程的区别
共同点:都属于io多路复用:一个线程同时管理和检测多个io是否就绪,都是同步io。
会创建一个红黑树和一个就绪队列
指针和d引用应该分别在什么场景使用
引用作为一个变量的别名,底层是怎么支持这个功能的
Proactor模式是一种异步I/O的设计模式,它允许程序直接发起一个异步I/O操作并立即返回,而不需要等待该操作完成。一旦I/O操作实际完成,系统会通知相应的完成处理程序(Completion Handler),该处理程序随后执行与该I/O操作相关的后续处理。以下是关于Proactor模式的详细解释:
模式构成
Handle 句柄:在操作系统中,常见的文件描述符,用来标识socket连接或打开的文件。
Asynchronous Operation Processor:异步操作处理器,负责执行异步操作,通常由操作系统内核实现,也可以被用户态线程或进程模拟。
Asynchronous Operation:异步操作,如异步的通过套接字句柄读写数据。
Completion Event Queue:完成事件队列,用于缓存已经完成的异步操作。
Proactor:完成分发器,定义抽象接口,从完成事件队列中取出异步操作的结果,并分发调用已完成事件的后续处理逻辑。
Completion Handler:完成事件接口,一般是由回调函数组成的接口。
Concrete Completion Handler:完成事件后的具体处理逻辑,实现接口定义特定的应用处理逻辑。
工作时序
初始化与注册:启动应用程序并注册异步操作完成后的回调操作。
异步操作:主程序调用异步操作处理器提供的异步操作接口。
操作执行:Asynchronous Operation Processor执行异步操作,完成后将结果放入事件完成队列。
结果分发:Proactor从完成事件队列中取出结果,分发到相应的完成事件回调函数处理逻辑中。
优势与不足
优势:
处理耗时长的I/O操作和并发场景时,性能较高。
一旦提交I/O操作,用户进程可以做其他任何操作,提高了用户进程的灵活性。
不足:
实现相对复杂,与基本的同步I/O相比,异步I/O在使用上可能不那么直观。
需要底层操作系统提供异步I/O操作的支持。
应用场景
Proactor模式常用于需要并发执行多个操作的网络服务器和客户端的开发中,如高性能Web服务器、数据库服务器等。在这些场景中,Proactor模式能够有效地利用操作系统支持的异步机制,简化并发编程,提高性能和效率。
示例
Windows IOCP(I/O Completion Ports):Windows操作系统提供的一个高性能的线程同步机制,专门为高吞吐量的服务器应用程序设计。
Linux io_uring:Linux内核中的一个比较新的接口,用于提供高效的异步I/O。
Boost.Asio C++库:一个跨平台的C++库,用于编写基于网络和低级I/O操作的程序,支持在Windows上通过IOCP和在POSIX系统上通过aio_calls或epoll实现异步I/O操作。
6、问了boost和beast的区别
Boost和Beast是两个在C++编程领域中经常一起提及的库,但它们各自有不同的定位和功能。
Boost是一个广泛使用的C++库集合,旨在提供对标准库的扩展和补充。它包含了众多经过高度优化和广泛测试的库,涵盖了从数据结构到算法,再到并发和网络编程等多个方面。Boost库以其高性能、灵活性和跨平台兼容性而闻名,是C++开发者中非常受欢迎的一套工具集。
Beast则是Boost库中的一个专门用于处理HTTP和WebSocket协议的库。它基于Boost.Asio(一个用于网络和底层I/O编程的C++库)构建,提供了对HTTP/1.1和HTTP/2以及WebSocket协议的解析、封装和序列化功能。Beast的设计目标是提供一个高效、易用且功能强大的网络协议处理库,使得开发者能够轻松地实现客户端和服务器端的网络通信。
区别
定位不同:Boost是一个综合性的C++库集合,包含了众多不同领域的库;而Beast则是Boost库中的一个专门用于处理HTTP和WebSocket协议的库。
功能范围:Boost的功能范围非常广泛,涵盖了数据结构、算法、并发、网络编程等多个方面;而Beast则专注于HTTP和WebSocket协议的处理。
依赖关系:Beast依赖于Boost.Asio库来实现网络通信功能,而Boost则是一个独立的库集合,不依赖于其他特定库(尽管它的一些库可能会依赖于标准库或其他Boost库)。
综上所述,Boost和Beast在C++编程领域中各自扮演着不同的角色。Boost是一个综合性的库集合,提供了广泛的C++编程工具;而Beast则是Boost中专门用于处理HTTP和WebSocket协议的库,为网络通信提供了高效、易用且功能强大的支持。
在大多数编程语言中,特别是像C、C++、Java高级编程语言,程序的“标准”入口点是main函数。当程序启动时,操作系统会调用main函数作为程序的起点。然而,确实有一些方法可以让程序从其他函数开始执行
1. 使用main函数作为跳板
最常见的方法是通过main函数作为程序的入口点,然后在main函数中调用你希望作为实际起点的函数。例如,在C语言中:
2. 使用链接器脚本(Linker Script)
在某些情况下,你可以通过修改链接器脚本来改变程序的入口点。这通常用于嵌入式系统或需要精确控制程序启动行为的场景。链接器脚本允许你指定程序的入口地址,这可以是任何有效的函数地址。
例如,在GCC中,你可以使用 -Wl,-T,your_linker_script.ld 选项来指定一个自定义的链接器脚本。链接器脚本中可能包含类似以下的条目:
3. 动态加载和执行代码(如插件系统)
在更高级的应用中,你可以通过动态加载和执行代码来改变程序的执行流程。例如,在C++中,你可以使用动态链接库(DLLs)或共享对象(SOs),在运行时加载它们并调用其中的函数。
4. 使用操作系统特性
在某些操作系统中,你可以利用特定的系统调用来改变程序的执行起点。例如,在Windows中,你可以使用CreateThread或CreateProcess来启动一个新线程或进程,并指定一个函数作为该线程或进程的入口点。
5. 特定框架或环境
一些编程框架或环境可能提供了改变程序入口点的机制。例如,在Web应用程序中,框架可能会处理HTTP请求并调用相应的处理函数,而不是从main函数开始。
这些方法通常涉及到对程序构建和链接过程的控制,或者利用操作系统和框架提供的特性。选择哪种方法取决于你的具体需求和目标环境。
- 析构函数是虚函数:cpp但凡涉及继承析构函数必是虚的
虚析构的场景在使用多态时,以父指针调用子类,此时如果单纯析构父指针会调用父析构,那么父类外的子类部分就没有被析构,所以声明父析构为虚析构,此时调用的析构则为子类的析构
介绍一个你最熟悉的算法?
答:贪心算法:又名贪婪法,是寻找最优解问题的常用方法,这种方法模式一股将求解过程分成若千个步骤,但每个步骤都应该应用贪心原则,选取当前状态下最好/最优的选择。
贪心法的基本步骤:
1从某个初始解出发;2.采用迭代的过程,当可以向目标进一步时,就根据局部最优策略,得到一部分解,缩小问题规模;3,将所有解综合起来
9.介绍数据结构中二叉树的排序算法?
答:学习的还行,二叉树算法排序的原则:1.选择第一个元素作为根节点2.之后如果元素大于根节点放在右子树,如果元素小于根节点,则放在左子树,最后按照中序遍历的方式进行输出,则可以得到排序的结果(左>根>右)(没介绍完技术面试官就打断了,说已经了解了基础很好)
反向代理和正向代理的区别,应用场景
反向代理和正向代理是代理服务器的两种不同使用方式,它们在部署位置、代理对象、作用目的以及应用场景等方面存在显著差异。
一、反向代理与正向代理的区别
部署位置与代理对象
正向代理:通常部署在客户端所在网络的边缘,如企业的局域网出口或用户的个人设备上。它代表客户端(用户)向远程服务器发起请求,客户端直接与正向代理交互,而服务器端只知道代理的存在,不知道真实的客户端信息。
反向代理:则部署在服务器端,通常靠近目标服务器或服务集群,作为公共入口点。它代表服务器接收客户端的请求,客户端不知道它实际上是在与代理服务器通信,而认为是直接与目标服务器通信。
作用目的
正向代理:主要解决访问限制问题,如匿名浏览和流量控制。它还能提供缓存功能,减少对外部资源的直接访问,提高访问速度。
反向代理:主要用于负载均衡,将客户端请求分布到后端服务器集群上,提高网站的可用性和扩展性。
客户端与代理的交互
在正向代理中,客户端需要配置代理设置,明确知道代理的存在。
在反向代理中,客户端通常不知道代理的存在,它直接访问的是代理服务器提供的公共地址。
二、反向代理与正向代理的应用场景
反向代理的应用场景
负载均衡:根据预设的算法,将请求分发到多个后端服务器上,以达到负载均衡的目的。
缓存加速:缓存静态内容或动态内容的结果,将来自目标服务器的响应缓存起来,并在后续的请求中直接返回缓存的响应,从而提高响应速度和减轻目标服务器的负载。
安全性与保护:用作安全层,保护目标服务器免受直接暴露在公共网络中的攻击。
域名和路径重定向:根据不同的域名或路径,将请求重定向到不同的目标服务器上。
正向代理的应用场景
提供匿名访问:屏蔽客户端的真实IP地址,使得客户端可以通过代理服务器访问目标资源,隐藏自己的身份。
突破网络限制:帮助用户绕过特定网站或内容的访问限制。
加速访问:缓存经常访问的资源,并在客户端请求时直接返回缓存的内容,从而加快访问速度。
安全性增强:过滤恶意请求、防止攻击和窃取敏感信息。例如,在企业内部,公司可以设置正向代理服务器来过滤员工对外访问的流量,保护内部网络的安全。
综上所述,反向代理和正向代理在多个方面存在显著差异,并且各自具有独特的应用场景。在实际应用中,需要根据具体需求选择合适的代理方式。
线程与进程是操作系统中两个重要的概念,它们在资源分配、执行方式、通信机制等方面存在显著的差异。
一、定义与特点
进程:是系统进行资源分配和调度的基本单位,它包含了执行一个程序所需的所有资源。
每个进程都有自己独立的内存空间和系统资源,这使得进程间具有较高的隔离性。
进程间通信需要通过特定的机制(如管道、共享内存、消息队列等)来实现,相对复杂且开销较大。
线程:是进程中的一个执行单元,它共享进程所拥有的资源,是CPU调度的基本单位。
线程间共享同一个进程的内存空间和系统资源,这使得线程间通信更加便捷,但也需要考虑同步和互斥问题以防止数据竞争和死锁。
线程的创建、销毁以及切换的开销通常比进程小,这使得线程更适合处理大量并发任务。
二、区别与联系
区别:
资源分配:进程是资源分配的基本单位,拥有独立的内存空间和系统资源;而线程是CPU调度的基本单位,共享进程的资源。
独立性:进程间具有较高的独立性,一个进程的崩溃影响其他进程的运行;而线程间由于共享进程的资源,一个线程的崩溃可能导致整个进程的崩溃(除非采取了相应的保护措施)。
开销:进程的创建、销毁以及切换的开销较大,因为涉及到操作系统资源的分配和释放;而线程的创建、销毁以及切换的开销较小,因为线程共享进程的资源,无需进行资源的重新分配和释放。
通信:进程间通信需要通过特定的机制来实现,相对复杂且开销较大;而线程间通信更加便捷,因为它们共享同一个进程的内存空间和系统资源。
联系:
线程是进程的一部分,一个进程可以包含多个线程。
线程和进程都用于实现并发编程,但线程更适合处理大量并发任务,因为它具有较小的开销和更高的执行效率。
三、应用场景
进程:
适用于需要隔离资源的独立任务,如服务器中多个应用程序的并行运行。
适用于分布式计算和集群计算,因为进程可以在不同的计算机上运行并通过网络进行通信。
线程:
适用于轻量级并发任务的场景,如计算密集型任务或需要共享资源的任务。
适用于多线程程序,可以充分利用多核处理器的并行计算能力来提高程序的执行效率。
四、注意事项
同步与互斥:由于线程间共享同一个进程的内存空间和系统资源,因此需要采取适当的同步和互斥措施来防止数据竞争和死锁问题。
线程安全:编写多线程程序时需要注意线程安全性,确保多个线程在并发执行时相互干扰导致程序崩溃或产生错误结果。
性能优化:虽然线程具有较小的开销和较高的执行效率,但过多的线程也会导致上下文切换频繁、系统资源紧张等问题。因此,在编写多线程程序时需要进行性能优化,合理控制线程的数量和优先级等参数。
综上所述,线程与进程在操作系统中扮演着不同的角色,具有各自的特点和应用场景。了解它们的区别与联系有助于我们更好地理解和使用并发编程技术来提高程序的执行效率和性能
死锁
死锁是什么
死锁是指两个或两个以上的进程(或线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(或线程)称为死锁进程(或死锁线程)。
具体来说,当多个进程(或线程)在并发执行时,如果每个进程(或线程)都持有某种资源而又等待其他进程(或线程)释放它所占有的资源,那么这些进程(或线程)就会陷入一个无限等待的循环中,即发生死锁。
如何避免死锁
为了避免死锁的发生,可以采取以下几种方法:
确保资源分配的顺序一致:
引入资源有序分配策略,要求每个进程(或线程)按照相同的顺序获取资源,从而避免进程(或线程)间因资源获取顺序不同而发生死锁。
避免资源的永久占用:
如果一个进程(或线程)已经占有了一些资源,但又申请了其他进程(或线程)当前持有的资源,导致两个进程(或线程)互相等待对方的资源释放,可以通过设置超时机制或者强制释放一部分已占有资源来避免死锁的发生。
使用资源分配图:
资源分配图可以帮助系统检测和避免死锁。图中的节点表示进程(或线程)和资源,边表示进程(或线程)对资源的请求和释放情况。系统通过检测图中是否存在环来判断是否有死锁,如果存在环,则可能发生死锁。
实现资源预先分配:
对于必须同时拥有多个资源才能进行的任务,可以采用资源预分配的策略,即进程(或线程)在开始执行之前就获取所有需要的资源,以确保没有其他进程(或线程)可以抢占这些资源导致死锁。
使用锁机制时遵循最佳实践:
在多线程编程中,使用锁(如互斥锁、读写锁等)来同步对共享资源的访问时,应遵循一些最佳实践,如尽量缩短锁的持有时间、避免嵌套锁等,以降低死锁的风险。
定期监控和诊断:
定期检查系统的性能指标、日志和错误信息,及时发现潜在的死锁问题。使用专门的工具(如Java中的jstack、MySQL的SHOW ENGINE INNODB STATUS等)来检测死锁线程(或进程)的状态和调用栈信息,从而及时发现和解决死锁问题。
综上所述,避免死锁需要从多个方面入手,包括确保资源分配的顺序一致性、避免资源的永久占用、使用资源分配图进行检测、实现资源预先分配、遵循锁机制的最佳实践以及定期监控和诊断等。这些措施的综合应用可以有效降低系统发生死锁的风险。
IO多路:
IO多路复用是一种高效的I/O处理方式,它允许单个线程同时监视多个文件描述符(socket),以处理多个并发连接。在Linux系统中,IO多路复用的三种主要实现方式是poll、epoll和select。以下是这三种方式的详细介绍:
1. select
工作原理:
创建一个文件描述符集合,用于存放需要监视的文件描述符(socket)。
调用select函数,将文件描述符集合传递给内核,让内核监视这些文件描述符的状态变化(如可读、可写、异常)。
select函数会阻塞当前线程,直到一个或多个文件描述符就绪,或者超时发生。
当select函数返回时,检查文件描述符集合,找出哪些文件描述符就绪,并进行相应的读写操作。
优缺点:
优点:将监视文件描述符是否就绪的工作由用户态放到了内核态,效率更高。
缺点:
可监控的文件描述符数量有限制(通常为1024个),这限制了最大能支持的并发数。
用户进程的文件描述符集合需要从用户进程拷贝到内核,有一定的性能开销。
select函数返回后,用户进程只知道某个或某几个文件描述符满足要求,但不知道是哪个,因此需要遍历所有文件描述符,复杂度为O(n)。
2. poll
工作原理:
与select类似,poll也使用一个结构体数组来存储需要监视的文件描述符及其事件。
调用poll函数,将结构体数组传递给内核进行监视。
poll函数同样会阻塞当前线程,直到有文件描述符就绪或超时发生。
返回后,用户进程需要遍历结构体数组,找出哪些文件描述符就绪
优缺点:
优点:解决了select的文件描述符数量限制问题,可以监视更多文件描述符。
缺点:
仍然存在文件描述符状态在用户态和内核态的频繁拷贝问题。
需要遍历所有文件描述符来检查哪些已经就绪,复杂度同样为O(n)。
只能在Linux平台使用,跨平台性较差。
3. epoll
工作原理:
epoll是select和poll的增强版本,它使用红黑树和链表来优化性能。
调用epoll_create函数创建一个epoll实例(红黑树的根节点)。
使用epoll_ctl函数向epoll实例中添加、修改或删除需要监视的文件描述符及其事件。
调用epoll_wait函数等待事件发生。当有文件描述符就绪时,epoll_wait会返回就绪的文件描述符数量及其相关信息。
优缺点:
优点:
没有文件描述符数量的限制。
避免了文件描述符状态在用户态和内核态的频繁拷贝。
只需遍历就绪的文件描述符集合,复杂度为O(1)。
支持水平触发和边缘触发两种模式。
缺点:只能在Linux平台使用,跨平台性较差(但这对大多数使用epoll的场景来说通常不是问题,因为epoll本身就是Linux特有的)。
综上所述,poll、epoll和select各有优缺点。在选择使用哪种方式时,需要根据具体的应用场景和需求进行权衡。例如,在需要跨平台支持且并发连接数较少的情况下,可以选择select;在Linux平台上且需要监视大量文件描述符时,epoll是更好的选择。
3、如何处理多线程中的互斥?
在多线程编程中,互斥(Mutex,全称Mutual Exclusion)是用来防止多个线程同时访问共享资源,从而避免数据竞争和不一致性的重要机制。
1. 使用互斥锁(Mutex)
互斥锁是最直接和常用的互斥机制。它允许一个线程锁定某个资源,从而阻止其他线程访问该资源,直到锁被释放。
创建和销毁:在大多数编程环境中,都有创建和销毁互斥锁的API。
加锁和解锁:线程在访问共享资源前需要加锁,访问完成后解锁。
死锁和优先级反转:使用互斥锁时需要小心死锁和优先级反转问题。
2. 使用读写锁(Read-Write Lock)
读写锁允许多个线程同时读取共享资源,但写入时只有一个线程可以访问。这提高了读取操作的并发性。
共享锁(读锁):允许多个线程同时持有。
排他锁(写锁):只有一个线程可以持有。
3. 使用信号量(Semaphore)
信号量是一种更通用的同步机制,可以看作是一个计数器,用于控制对共享资源的访问。
初始化:信号量通常被初始化为一个正整数,表示可用的资源数量。
等待(P操作):线程在访问资源前需要等待信号量,如果信号量大于0,则减一并继续;否则阻塞。
信号(V操作):线程在访问完资源后释放信号量,即加一,并可能唤醒等待的线程。
4. 使用条件变量(Condition Variable)
条件变量用于线程间的同步,它允许线程等待某个条件成立。
等待:线程可以在条件变量上等待,直到被其他线程唤醒。
通知:线程在满足某个条件时可以通知等待的线程。
5. 使用原子操作(Atomic Operations)
原子操作是不可被中断的操作,它们通常用于实现低级的同步机制。
原子读/写:确保读/写操作是原子的,即不可被其他线程中断。
原子比较并交换(CAS):比较并交换操作,如果当前值与预期值相等,则设置为新值。
6. 使用高级并发数据结构
一些编程语言或库提供了高级的并发数据结构,如并发队列、并发哈希表等,它们内部已经实现了必要的同步机制。
注意事项
避免死锁:确保每个线程都能最终释放它持有的锁。
避免优先级反转:高优先级的线程不应该被低优先级的线程阻塞。
减少锁粒度:尽量缩小锁的范围,以减少锁竞争和提高并发性。
使用锁超时:在尝试获取锁时设置超时,以避免线程无限期地等待。
考虑性能影响:锁的使用会影响性能,因此应该仔细权衡同步需求和性能要求。
通过合理地使用上述机制,可以有效地处理多线程中的互斥问题,确保程序的正确性和性能。
如何处理tcp的粘包问题
TCP粘包问题是指在TCP传输过程中,由于TCP是基于字节流的传输协议,没有消息边界的概念,因此当发送方发送的多个数据包到达接收方时,可能会粘成一个数据包,导致接收方无法正确区分每个数据包。以下是一些处理TCP粘包问题的方法:
一、发送端处理
定长发送:
发送端在发送数据时,将每个数据包都设置为固定长度。如果数据长度不足,则通过填充空字节来补齐。
接收端在接收数据时,也按照固定长度来读取数据。
这种方法实现简单,但可能会浪费带宽,特别是在数据长度变化较大的情况下。
尾部标记序列:
在每个数据包的尾部添加一个特殊的字节序列作为标记,用来标示数据包的末尾。
接收端在接收数据时,通过检测这个标记来确定数据包的边界。
这种方法需要确保标记序列的唯一性和不可预测性,以避免与数据内容冲突。
头部标记分步接收:
在每个数据包的头部添加一个长度字段,用来标示该数据包的总长度。
接收端在接收数据时,首先读取头部信息,然后根据长度字段的值来读取整个数据包。
这种方法相对灵活,能够处理不同长度的数据包,但需要在每个数据包中添加额外的头部信息。
禁用Nagle算法:
Nagle算法是TCP中用来减少小数据包数量的一种优化算法,但可能会导致粘包问题。
通过设置TCP_NODELAY选项来禁用Nagle算法,可以减少粘包的发生。
二、接收端处理
缓冲区管理:
接收端在接收数据时,需要合理管理缓冲区,确保能够及时读取和处理数据。
如果缓冲区过小,可能会导致数据丢失;如果缓冲区过大,可能会增加处理延迟和内存占用。
消息定界符:
在接收端,通过检测特定的字符或字符序列作为消息的定界符,来确定数据包的边界。
这种方法需要确保定界符的唯一性和不可预测性,以避免与数据内容冲突。
应用层协议设计:
在设计应用层协议时,可以明确数据包的格式和边界标识。
例如,可以在每个数据包的头部添加长度字段和校验和等信息,以确保数据的完整性和正确性。
拆包和重组:
在接收端,如果检测到粘包问题,可以通过拆包和重组的方式来恢复原始数据包。
这通常需要根据应用层协议的具体规定来实现。
三、综合处理
选择合适的传输协议:
根据具体应用场景的需求,选择合适的传输协议。如果可靠性要求较高,可以选择TCP;如果实时性要求较高,可以考虑使用UDP或其他实时传输协议。
优化网络性能:
优化网络性能,减少网络延迟和抖动,可以降低粘包问题的发生概率。
例如,可以通过增加网络带宽、优化网络拓扑结构等方式来提高网络性能。
使用现成的库和框架:
在实际开发中,可以使用现成的网络通信库和框架来处理TCP粘包问题。这些库和框架通常已经实现了完善的粘包处理机制,并提供了易于使用的API接口。
综上所述,处理TCP粘包问题需要从发送端、接收端以及综合处理三个方面入手。通过合理选择传输协议、优化网络性能、使用现成的库和框架以及实现有效的粘包处理机制等方法,可以有效地解决TCP粘包问题。
4. 状态机里如攻击状态,如果有多个攻击形式怎么实现
1. 为什么析构函数必须是虚函数
2. 编译器调试时看到的地址是物理地址还是逻辑地址
1内联函数
2.如何最快判断一个数是不是2的n次方(位运算)
3. 数据流中找出中位数,运用大根堆和小根堆
cpp指定内存位置new,虚拟继承 装箱拆箱
观察者的多观察者模式,cmake,快排撕错了,交换和临界条件,
数据库事件是什么;计网HTTPS和http区别以及HTTPS如何具体加密过程
什么是线程安全;主线程中的资源可以加锁吗;死锁原因和防范;除了宏定义还有什么方法能够解决头文件重复引用
电子邮箱是UDP还是TCP;debug和release文件区别;debug调试内存泄露;静态库和动态库区别;数据库的不同范式
内存泄露和避免
第一题:给一个数n,输出从1到n的所有可能的弹栈序列
第二题:工人做任务,每个任务消耗一定体力值,给了工人体力值数组和任务消耗值数组。还有n个可以补充v能力的道具,问最多可以完成几个任务第三题:甚至来不及看
回溯,给你一个数n,生成n*n的矩阵,有元素1,和0,不能出现孤岛0(即没有与边界联通的区域)
c++中没有decimal类型;udp因为不是面向连接的,所以可以多对象连接
做标题忘记怎么输出小数位数 printf("% . 2lf",double);
C语言的内存分区;线程同步的四种方式;
问了一个二维数组的存储读取怎么遍历更快。
流量控制和拥塞控制有点混淆;switch case的default细节;http状态码细节;记忆化搜索,暴力搜过了70;#pragma pack(1)
问了友元,间址运算符
C++编译过程,
怎样减小贴图带宽,第一道算法dpA了 第二道求解九宫格拼图 | 8Puzzle |,用dfs只过了26%,一直剪纸但没找到什么好办法。应该采用bfs,对于dfs需
拥塞控制算法;tcp udp http和smtp 哪个适合游戏数据传递 没看过smtp
多比特
动态分配和静态分配内存;animation layer和 blendtree对于动画切换和响应的作用;红黑树性质
TCP/IP四层模型;正整数越界变负数的计算。无感谢信
一面复盘了笔试问题,问了项目,问了cpp的虚函数。操作系统页表,进程线程,中断,指针引用,虚函数,AVL树,排序算法,
一面:虚拟继承,如果有一个背包系统想要快速显示比如武器类就只显示武器类,怎么设计数据结构,回答枚举。
一面:生命周期函数;值类型引用类型;ugui的布局;对象池的缺点;dictionary底层;协程的缺点,调用时机
SVN操作问的较多,问了点git, set和List区别。
一面:c++: static 很深入问了一堆;头文件中static答错了;各种空类的大小,如果是虚继承的情况
c++dynamic_cast 能转换成功的原因RTTI(笔试也考了四种转换;float 底层结构
memcpy和memmove 答错了;map和unordered_map区别
设计一个哈希函数,键是任意的string,给一百个空间,怎样设计更好(回答的是求和取余127感觉应该取99)。
生产者消费者模式是一种常见的设计模式,它用于解决在多线程环境下,一个或多个生产者线程生成数据,一个或多个消费者线程消费数据的同步问题。这种模式通过平衡生产者和消费者之间的速度差异,确保数据的有效传递和系统的稳定运行。以下是对生产者消费者模式管理的详细探讨:
一、基本概念
生产者(Producer):负责生成数据,并将其放入缓冲区(Buffer)中供消费者使用。生产者线程通常会在生成数据后,将数据放入共享缓冲区,并通知消费者有新数据可供消费。
消费者(Consumer):负责从缓冲区中取出数据,并进行处理。消费者线程通常会在缓冲区中有数据时,取出数据进行处理,并在处理完成后通知生产者缓冲区已空,可以继续生产新数据。
缓冲区(Buffer):用于存储生产者生成的数据,供消费者使用。缓冲区通常是一个先进先出的队列(FIFO Queue),以确保数据的顺序性。
二、管理策略
同步机制:
使用互斥锁(Mutex)来确保对缓冲区的访问是线程安全的。即,在同一时间内,只有一个线程可以访问缓冲区。
使用条件变量(Condition Variable)或信号量(Semaphore)来协调生产者和消费者之间的同步。当缓冲区满时,生产者会等待消费者消费数据;当缓冲区空时,消费者会等待生产者生产数据。
缓冲区大小:
缓冲区的大小是一个重要的设计参数。如果缓冲区太小,可能会导致生产者频繁等待消费者,降低生产效率;如果缓冲区太大,可能会占用大量内存资源,并增加消费者处理数据的延迟。
缓冲区的大小应根据实际应用场景进行权衡和选择。
生产者和消费者的数量:
在多线程环境下,可以根据系统资源和性能需求,灵活地配置生产者和消费者的数量。
如果生产者数量多于消费者数量,可能会导致缓冲区快速填满,增加等待时间;如果消费者数量多于生产者数量,可能会导致缓冲区中的数据被快速消费完,增加消费者的等待时间。
因此,应根据实际情况合理配置生产者和消费者的数量,以实现最佳的性能和资源利用率。
异常处理:
在生产者消费者模式中,应充分考虑异常处理机制。例如,当生产者无法生成数据时,应通知消费者停止消费;当消费者无法处理数据时,应通知生产者停止生产。
同时,还应考虑在异常情况下对缓冲区的清理和释放工作,以避免资源泄露和内存泄漏等问题。
三、实现方式
生产者消费者模式可以通过多种方式实现,如使用C++中的std::thread、std::mutex、std::condition_variable等标准库组件
在实现时,应注意以下几点:
确保对缓冲区的访问是线程安全的。
正确地使用同步机制来协调生产者和消费者之间的同步。
根据实际需求合理配置缓冲区大小和生产者消费者的数量。
充分考虑异常处理机制,确保在异常情况下能够正确地释放资源和处理数据。
通过以上策略和管理方式,可以有效地实现生产者消费者模式,提高系统的性能和稳定性
互斥,信号量,条件变量的区别
互斥、信号量、条件变量是多线程编程中用于同步和互斥的重要概念,
互斥(Mutex)是一种用于保护临界区的同步机制,确保同一时刻只有一个线程可以访问临界区。
状态:互斥量只有两种状态:已加锁和已解锁。
访问方式:独占访问,即当一个线程持有互斥量时,其他线程必须等待直到该线程释放互斥量。
所有者概念:互斥量有所有者的概念,通常是由加锁的线程持有并解锁。
应用场景:适用于需要严格互斥访问的场景,如保护共享数据不被多个线程同时修改。
信号量(Semaphore)是一种更通用的同步机制,可以用于实现线程间的同步和互斥访问。
状态:信号量的状态由一个计数器表示,可以初始化为大于1的值,表示可用的资源数量。
访问方式:信号量允许多个线程同时访问(只要计数器大于0),当一个线程访问时,计数器减1;当线程释放资源时,计数器加1。
所有者概念:信号量没有所有者的概念,它是基于计数器的机制。
应用场景:适用于需要控制对多个资源的访问的场景,如限制同时访问某个资源的线程数量。
条件变量(Condition Variable)
定义:条件变量是一种用于线程同步的机制,它允许线程在某个条件满足时进行等待,并在条件满足时被唤醒。
状态:条件变量本身没有状态,它依赖于与之关联的互斥量和共享数据状态。
访问方式:条件变量通常与互斥量一起使用,以确保线程在访问共享数据时引发竞态条件。线程在调用wait方法时会释放互斥量并进入等待状态,直到另一个线程调用notify_one或notify_all方法唤醒它。
应用场景:适用于线程间需要基于某个条件进行同步的场景,如生产者-消费者模型中生产者等待缓冲区有空间、消费者等待缓冲区有数据。
关键点总结
互斥量:主要用于保护临界区,确保同一时刻只有一个线程可以访问。
信号量:更通用的同步机制,可以用于实现线程间的同步和互斥访问,允许多个线程同时访问资源(只要资源数量允许)。
条件变量:用于线程间的同步,允许线程在特定条件下等待和唤醒,通常与互斥量一起使用以确保线程安全。
在实际编程中,应根据具体的需求和场景选择合适的同步机制来实现线程间的同步和互斥
extern是C和C++语言中的一个关键字,主要用于声明变量或函数是在当前源文件外部定义的
一、extern的作用
声明外部变量:
当一个全局变量在另一个源文件中定义时,如果想在当前源文件中访问该变量,可以使用extern关键字进行声明。这告诉编译器该变量的定义在其他文件中,当前文件只是引用它。
声明外部函数:
同样地,如果一个函数在另一个源文件中定义,而当前源文件需要调用该函数,也可以使用extern进行声明(尽管对于函数来说,extern是可选的,因为函数默认就是extern的)。这有助于在多个文件中共享函数的声明,而不必在每个文件中都重新定义函数。
堆和栈
进程和线程有什么区别?
进程和线程是操作系统中用于实现并发执行和资源管理的两个重要概念,它们之间存在一些显著的区别。以下是对进程和线程区别的详细解释:
一、定义与概念
进程:进程是资源分配的基本单位,是系统进行资源分配和调度的独立实体。进程是程序在某个数据集合上的一次运行活动,是程序执行的一个实例。每个进程都有自己独立的地址空间和系统资源,如内存、文件、设备等。
线程:线程是CPU独立运行和独立调度的基本单位,是进程中实际运行的单位。一个进程可以包含多个线程,这些线程共享进程的资源,如内存和文件句柄等。线程的执行是并发的,即多个线程可以在同一时间内交替执行。
二、资源占用与开销
资源占用:进程拥有独立的地址空间和系统资源,因此每个进程都需要占用一定的系统资源。而线程共享进程的地址空间和资源,因此线程的资源占用相对较少。
开销:由于进程拥有独立的地址空间和资源,因此在进程切换时需要保存和恢复大量的上下文信息,导致进程切换的开销较大。而线程切换时只需要保存和恢复线程的上下文信息,因此线程切换的开销相对较小。
三、独立性与共享性
独立性:进程之间具有独立性,一个进程的崩溃影响其他进程的运行。而线程之间共享进程的地址空间和资源,因此一个线程的崩溃可能会导致整个进程的崩溃。
共享性:线程之间可以共享进程的地址空间和资源,这使得线程间的通信和数据共享变得更加容易。而进程之间需要通过进程间通信(IPC)机制来实现数据的共享和通信。
四、并发执行与效率
并发执行:进程和线程都可以实现并发执行,但线程的并发执行效率更高。因为线程切换的开销较小,且线程之间可以共享资源,减少了资源竞争和同步的开销。
效率:由于线程切换的开销较小且可以共享资源,因此线程在并发执行时通常比进程具有更高的效率。然而,这也增加了线程管理的复杂性,因为需要处理线程间的同步和互斥问题。
五、应用场景
进程:进程适用于需要独立运行、资源占用较大且对安全性要求较高的应用场景。例如,在操作系统中运行不同的用户程序时,通常会使用进程来隔离不同的程序运行空间。
线程:线程适用于需要高效并发执行、资源共享且对安全性要求相对较低的应用场景。例如,在编写多线程程序时,可以使用线程来实现任务的并发执行和数据共享。
综上所述,进程和线程在定义、资源占用、独立性、共享性、并发执行效率以及应用场景等方面都存在显著的区别。在选择使用进程还是线程时,需要根据具体的应用需求和系统环境进行权衡和选择。
进程通信和线程通信区别
进程通信和线程通信是操作系统中两种不同的通信方式,它们之间存在一些显著的区别。以下是对进程通信和线程通信区别的详细解释:
一、通信主体与共享资源
进程通信:
通信主体:进程是资源分配的基本单位,每个进程都有自己独立的地址空间和系统资源。因此,进程间的通信需要借助特定的通信机制。
共享资源:进程间不直接共享内存和变量,需要通过进程间通信(IPC)机制来实现数据的共享和通信。
线程通信:
通信主体:线程是CPU独立运行和调度的基本单位,线程之间共享进程的地址空间和资源。因此,线程间的通信相对简单,可以直接通过共享内存和变量来实现。
共享资源:线程间可以直接访问和修改共享内存中的变量和数据结构,无需额外的通信机制。
二、通信机制与方式
进程通信机制:
管道(Pipe):一种半双工的通信方式,数据只能单向流动,且只能在具有亲缘关系的进程间使用。
有名管道(Named Pipe):允许无亲缘关系进程间的通信,但仍然是半双工的。
信号量(Semaphore):用于控制多个进程对共享资源的访问,实现进程间的同步和互斥。
消息队列(Message Queue):允许进程间通过消息进行通信,消息可以是有格式的,克服了管道只能承载无格式字节流的缺点。
共享内存(Shared Memory):映射一段能被其他进程所访问的内存,是最快的IPC方式,但需要配合其他通信机制(如信号量)来实现同步和通信。
套接字(Socket):可用于不同设备及其间的进程通信,是一种更为通用的进程间通信机制。
线程通信机制:
共享内存:线程间可以直接通过读写共享内存中的变量来进行通信。
锁机制:包括互斥锁、条件变量、读写锁等,用于保证线程间的同步和互斥,防止数据竞争和不一致。
信号量机制:类似于进程间的信号量,但用于线程间的同步和互斥。
消息传递:虽然线程间可以直接通过共享内存通信,但有时也会采用显式的消息传递方式(如使用条件变量和信号量配合实现)。
三、通信复杂度与开销
进程通信:
复杂度:由于进程间不直接共享内存和变量,需要通过特定的通信机制来实现数据的共享和通信,因此进程通信的复杂度相对较高。
开销:进程间通信需要操作系统内核的参与和调度,因此通信开销较大。
线程通信:
复杂度:线程间可以直接通过共享内存和变量进行通信,无需额外的通信机制,因此线程通信的复杂度相对较低。
开销:线程间通信不需要操作系统内核的频繁参与和调度,因此通信开销较小。
四、应用场景与需求
进程通信:
应用场景:适用于需要独立运行、资源占用较大且对安全性要求较高的应用场景。例如,在操作系统中运行不同的用户程序时,通常会使用进程来隔离不同的程序运行空间,并通过进程间通信机制来实现数据的共享和通信。
需求:需要高效的进程间同步和互斥机制来确保数据的一致性和安全性。
线程通信:
应用场景:适用于需要高效并发执行、资源共享且对安全性要求相对较低的应用场景。例如,在编写多线程程序时,可以使用线程来实现任务的并发执行和数据共享。
需求:需要简单的线程间同步和互斥机制来确保数据的正确性和一致性。
综上所述,进程通信和线程通信在通信主体、共享资源、通信机制与方式、通信复杂度与开销以及应用场景与需求等方面都存在显著的区别。在选择使用哪种通信方式时,需要根据具体的应用需求和系统环境进行权衡和选择。
一、容器类型与插入效率
vector是一个变长一维数组,连续存放的内存块,支持高效的随机访问。
插入效率:
在尾部插入元素时,由于vector预分配了额外的内存空间(通常是以倍增或1.5倍增的方式),所以在大多数情况下插入操作是常数时间复杂度O(1)的(均摊后)。
但在中间或开始位置插入元素时,需要移动其他元素以腾出空间,这会导致线性时间复杂度O(n)的插入操作。
list是一个双向链表,内存空间上可能是不连续的,不支持随机存取。
插入效率:在任何位置插入元素时,只需调整指针即可,因此是常数时间复杂度O(1)的插入操作。
set是一个有序集合,使用平衡二叉树(如红黑树)存储,不支持直接存取元素,元素按排序规则自动排序。
插入效率:插入元素时,需要找到合适的位置并保持树的平衡,这通常是O(log n)的时间复杂度。但由于不需要内存拷贝和内存移动(节点以指针方式存储),相对于vector在中间或开始位置插入时的效率要高。
二、vector容器的扩容机制
扩容方式:
vector通常使用倍增或1.5倍增的扩容策略。例如,当元素数量达到当前容量时,会分配一个新的、更大的内存块,并将旧内存块中的元素复制到新内存块中。
扩容次数与元素拷贝:
扩容次数与元素数量成对数关系(log2N),每次扩容都会引起元素拷贝。
总的操作次数(包括元素拷贝和插入)在均摊后是O(1)的(因为扩容次数较少,且每次扩容后容量翻倍)。
扩容开销:
虽然扩容时会有一定的开销(包括内存分配和元素拷贝),但由于扩容次数较少且每次扩容后容量显著增加,因此总体上vector的插入效率仍然较高。
综上所述,对于插入效率的要求:
如果需要在vector的尾部频繁插入元素,且对内存使用效率要求不高(因为会有一定的内存浪费在预分配的空间上),则vector是一个不错的选择。
如果需要在任意位置频繁插入元素,且希望保持元素的有序性,则set可能更适合。
如果需要在任意位置频繁插入和删除元素,且不关心元素的顺序,则list可能是一个更好的选择。
函数参数设计使用引用或右值引用提高传递效率,减少不必要的拷贝
1. 线性表和链表的区别?
内存空间连续,插入和访问
2. 一个数组拷贝到另外一个数组,怎么降低拷贝时间?
类型的话考虑移动语义,避免复制,如果是整形数组的话,考虑SIMD
拷贝的过程当中有哪些基本操作?cpu-cache-内存
除了simd还有其他方法吗?不知道
3. 对计算机系统结构有了解吗?说下对CPU架构的理解吧
cpu当中至少包括fetch code单元,alu计算单元,context(上下文,保存线程状态,寄存器单元,还有一些基本的存储空间(?)),cache(MESI)
4. cpu保持运行需要的基本单元有哪些?
fetch code, alu, context
5. 你对simd有了解吗?simd与openmp有什么区别呢?
simd是底层实现,openmp是上层的封装,实际底层实现不一定是用simd
6. 你对进程和线程有了解吗?
进程:分配资源的基本单位
线程:执行的基本单位
7. C++多态了解吗?
静态多态(函数重载),动态多态(虚函数机制)
8. 用纯C实现怎么实现多态?
(怎么不多想呢,别急),还是
9. 如何用整数值找到一个函数(?)
反问整数是啥,索引?(可以当作索引)
哈希函数当中的索引?根据哈希函数计算到对应位置
10. 你刚刚提到了哈希,你会怎么实现哈希?
扯了基础的哈希结构(线性哈希?)
然后说了数据库当中实现的动态哈希
14. 高性能计算有兴趣吗?如果给你一个矩阵*向量,你会怎么去优化他?
15. 矩阵乘法的复杂度多少?(n^3) 矩阵n*n
16. 反问:继续深入高性能学习要怎么学习?可以看刘文志的书
17. 高性能计算有什么吸引了你?
3.这段代码执行会发生什么
```C++
#include
class A {
public :
void f() {
}
};
int main() {
A* a = nullptr;
a -> f();
return 0;
}
```
(一开始在我看来,这就是未定义行为,应该是指向内存空间中的保留区或者是任意的一个地址;之后我查了,是因为类的成员函数保存在代码段,然后被所有对象所共享吗?然后没有通过this指针去访问类中的相应成员变量,所以仍然是可以调用的嘛)
11.计算这个类的大小
#include
class base1{
private:
int a;
char c;
public:
virtual void fun1(){}
virtual void fun2(){}
};
int main() {
std::cout << (sizeof(base1));
return 0;
}
19.写一个unique_ptr
比如OCI访问oracle数据库实习主要工作(说的api相关,图像分割相关内容)
Halcon问的也还好,OpenCV也涉及,图像处理知识要过关
设计到白盒测试相关工作,还有多线程的
介绍了项目开发要涉及的理论,这边怕忘记用小本本记了,然后针对他说的实事求是说了自己所接触过和未涉及的东西(PLC,传感器,硬件,软件算法,管理系统,3D点云)
该岗位目前围绕的产品与数控系统和五轴数控有很大关联,所以下面小心了
CNC,PLC,C#,.Net基本都要懂的,招进去也是要往C#转的