目录
C++语言篇 1
1、谈谈你对命名空间的理解 1
2、谈谈指针和引⽤的区别 1
3、谈谈你对内联函数的理解 1
4、谈谈函数的重载条件 1
5、谈谈c与c++中struct的不同点 2
6、如何理解c++的封装性 2
7、谈谈你对c++构造与析构的理解 2
8、构造函数的分类 2
9、构造函数的调⽤规则 2
10、谈谈你对浅拷⻉与深拷⻉的区别 3
11、谈谈啥叫对象成员以及对象成员的构造函数调⽤⽅式 3
12、谈谈你对explicit的理解 3
13、谈谈c中malloc free 与 c++中的new delete有啥区别,或则c++在堆区申请对象时为啥推荐使⽤new delete 4
14、谈谈你对static静态成员变量的理解 4
15、谈谈你对static静态成员函数的理解 4
16、谈谈你对this的理解 5
17、谈谈你对友元的理解 5
18、谈谈你对继承的理解 5
19、谈谈继承中的构造与析构的顺序 5
20、继承中同名成员的处理⽅法 5
21、哪些函数是⽆法继承的 6
22、静态多态与动态多态的区别 6
23、c++的动态捆绑机制是怎样的? 6
24、多态成⽴的条件 6
25、虚析构的作⽤ 7
26、纯虚析构与⾮纯虚析构的区别 7
27、谈谈重载、重写、重定义的概念 7
28、谈谈你对模板的理解 8
29、函数模板和普通函数的区别 8
30、函数模板和普通函数同时出现时的调⽤机制 8
31、谈谈静态转换(static_cast)、动态转换(dynamic_cast)、常量转换(const_cast)、重新解析转换(reinterpret_cast)的区别 8
32、谈谈你对异常的理解 9
33、谈谈c++的异常机制相⽐c语⾔的异常处理的优势 9
C语⾔篇 10
1、描述⼀下gcc的编译过程? 10
2、内存的最⼩存储单位以及内存的最⼩计量单位分别是? 10
3、#include<> 与#include ""的区别? 10
4、描述⼀下变量的命名规则 10
5、变量的声明与定义有啥区别? 10
6、谈谈c语⾔中有符号和⽆符号的区别? 10
7、谈谈计算机中补码的意义 10
8、谈谈数组的特点 10
9、数组的分类 10
10、描述⼀下⼀维数组的不初始化、部分初始化、完全初始化的不同点 10
11、谈谈数组名作为类型、作为地址、对数组名取地址的区别? 11
12、谈谈你对⼆维数组在物理上以及逻辑上的数组维度理解 11
13、描述⼀下函数的定义与函数的声明的区别 11
14、描述⼀下指针与指针变量的区别指针: 11
15、描述⼀下32位或64位平台下指针的⼤⼩ 11
16、描述⼀下指针数组的概念 11
17、描述⼀下普通局部变量、普通全局变量、静态局部变量、静态全局变量的区别 11
18、描述⼀下内存分区 11
19、在使⽤realloc给已分配的堆区空间追加空间时需要注意啥? 12
20、结构体与共⽤体的区别是啥 12
21、谈谈⽂件的分类 12
22、⽂件缓冲区刷新⽅式有⼏种 12
23、哪些情况下会出现野指针 12
24、如何理解指针作为函数参数的输⼊和输出特性 12
25、如何理解结构体的浅拷⻉与深拷⻉ 12
26、描述⼀下结构体对⻬规则 12
28、如何理解库函数 13
物联网篇: 14
1、RFID了解多少? 14
2、请问s50卡的默认密码是多少? 14
3、请问zigbee是⽤的什么平台? 14
4、你们使⽤zigbee模块是基于AT指令模式,还是协议栈模式? 14
5、zigbee的物理信道有⼏个? 14
6、zigbee协议栈规定了模块的⼏种通信⻆⾊? 14
7、MQTT是什么? 14
8、你们⽤的是MQTT那个版本? 14
9、消息订阅发布可以使⽤通配符,请问具体怎么使⽤有什么需要注意的? 14
10、NB-IOT是什么? 14
11、NB-IOT主要解决了什么问题? 14
12、你⽤的是什么NB-IOT模块,都⽀持哪些协议? 14
BS开发篇 15
1、什么是B/S架构 15
2、B/S 架构 与 C/S 架构对⽐ 15
3、什么是Javascript 15
4、描述⼀下ajax⼯作流程 15
数据库篇 16
1、谈谈你对数据库中事务的理解 16
2、谈谈你对数据库中联结表的理解 16
3、谈谈你对数据库中触发器的理解 16
4、谈谈你对数据库中索引的理解 16
5、谈谈你对数据库中主键、唯⼀约束、检查约束的理解 16
STL标准模板库篇 17
1、谈谈STL的六⼤组件 17
2、谈谈STL六⼤主键的关系 17
3、vector容器相⽐于普通的数组array有啥优点 17
4、deque容器(双端动态数组)相对于vector容器(单端动态数组)的区别 17
5、谈谈stack容器的概念 (栈容器) 18
6、谈谈queue容器的概念 (队列容器) 18
7、谈谈list容器的概念 18
8、谈谈set/multiset容器的概念 18
9、谈谈map/multimap容器的概念 18
10、谈谈什么是函数对象 18
11、谈谈什么是谓词 18
12、常⽤的遍历算法有哪些 19
13、常⽤的查找算法有哪些 19
14、常⽤的排序算法有哪些 19
15、常⽤拷⻉和替换算法 19
16、常⽤算法⽣成算法 19
17、常⽤集合算法 20
基础及系统编程技术篇 21
1、shell是什么 21
2、常⻅的 shell 及查看 shell 的⽅法 21
3、简单描述⼀下linux下的⽬录结构 21
4、linux下的权限分为哪3个组 21
5、linux下⽂件的类型 21
6、描述⼀下软连接和硬链接的区别 21
7、谈谈你对⽂件描述表的理解 21
8、谈谈你对程序和进程的理解 21
9、谈谈你对并⾏和并发的理解 22
10、谈谈你对MMU(内存管理单元)的理解 22
11、谈谈你对PCB(进程控制块)的理解 22
12、进程的状态有⼏种 22
13、谈谈你对fork创建的进程的数据共享模式 22
14、请说出啥叫僵⼫进程以及孤⼉进程 23
15、进程间的通信⽅式有⼏种?它们之间的特点? 23
16、谈谈守护进程的特点以及创建过程 23
17、多进程和多线程的区别 23
18、能够⽤exit退出某个线程? 24
19、谈谈你对线程分离的理解 24
20、谈谈你对同步互斥的理解 24
21、同步互斥的⽅式有哪些? 24
⽹络编程篇 25
1、及如何看待协议这个概念? 25
2、⽹络程序的设计模式有哪些? 25
3、谈谈⽹络的分层结构七层模型: 25
4、我们是如何知道要前往的下⼀块⽹卡的mac地址(⽬ 的mac地址)? 25
5、如果判断⼀个⽹段的⼴播地址以及⽹段地址? 26
6、分别描述⼀下IP地址以及端⼝的作⽤ 26
7、谈谈你对字节序的理解 26
8、描述⼀下三次握⼿以及四次挥⼿的过程 26
9、描述⼀下TCP与UDP的区别 26
10、谈谈select的优缺点 26
11、谈谈poll相较于select的优点 27
12、谈谈不同局域⽹的通信 27
13、谈谈集线器、交换机、路由器的作⽤ 27
Qt篇 27
1、谈谈信号和槽机制 27
C++语言篇
1、谈谈你对命名空间的理解
namespace 是⼀个关键字:随着⼯程量的增加,变量命名上不可避免的会出现重名,防⽌名称冲突
(在两个不同的命名空间中,即使2个变量名相同,也是2个不同的变量),在实际⼯作中,基本都使⽤ 标准命名空间。
命名空间只能全局范围内定义,不能定义在函数内部。
命名空间内,可以存放 变量、函数、结构体、类 ;也可以嵌套其他的命名空。
命名空间可以匿名(但⼀般不这样使⽤),类似静态全局变量。
命名空间是可以起别名.
2、谈谈指针和引⽤的区别
引⽤是给变量起别名,内部实现是指针常量(int* const ref = &a),其可以简单的理解为本体指
针存放的是变量的地址
引⽤的本质是指针常量,其指向不可修改,⽽指针可以改变指向
引⽤创建的同时必须初始化,指针创建的时候可以不必初始化
引⽤不能为空,指针可以为 NULL
“引⽤变量 ref”的内存单元保存的是“被引⽤变量 a”的地址 sizeof(引⽤) = 指向变量的⼤⼩ sizeof(指针) = 指针本身的⼤⼩
引⽤使⽤的时候⽆需解引⽤,指针需要解引⽤
指针和引⽤“⾃增/⾃减运算”意义不⼀样
在同等需求下,函数传参,引⽤可以将⼀级指针
3、谈谈你对内联函数的理解
(1)宏函数的缺陷:
需要加括号实现,以保证优先级的完整性
即使加了括号,有些情况依然有缺陷
(2)在普通函数前面加上inline关键字使其成为内联函数
(3)内联函数本身是一个函数 宏函数不是
(4)内联函数具有普通函数所有的行为 唯一不同之处在于 :内联函数会在适当的地方向宏函数一样展开,可以以空间换时间,内联函数即可以避免宏函数的缺陷,又可以避免普通函数入栈时的时间浪费
(5)在任何类内定义的函数自动成为内联函数
(6)下列情况,普通函数即使指定为内联函数,编译器也可能考虑不按内联编译 1)存在任何形式的循环语句 2)存在过多的条件判断语句 3)函数体过于庞⼤ 4)对函数进⾏取址操作
(7)1)内联仅仅只是给编译器⼀个建议,编译器不⼀定会接受这种建议 2)如果你没有将函数声明为内联函数,那么编译器也可能将此函数做内联编译(⼀个好的编译器将会内联⼩的、简单的函数)因此,不⽤刻意使⽤内联函数,可以交给编译器去⾃⾏处理。
4、谈谈函数的重载条件
在c语言中函数名必须是唯一的,程序中不允许出现同名函数。
在c++中允许函数重名,即在同一作用域内,具有相同的函数名,不同参数列表的一组函数,称为函数重载。
函数重载的条件:同一作用域:参数得类型,个数,顺序不同。
函数重载实现的原理:
编译器为了实现函数重载,也是默认为我们做了⼀些幕后的⼯作,编译器⽤不同的参数类型来修饰不同的函数名,⽐如void func(); 编译器可能会将函数名修饰成func,当编译器碰到void func(int x),编译器可能将函数名修饰为func_int,当编译器碰到void func(int x,char c),编译器可能会将函数名修饰为 _func_int_char我这⾥使⽤”可能”这个字眼是因为编译器如何修饰重载的函数名称并没有⼀个统⼀的标准,所以不同的编译器可能会产⽣不同的内部名
5、谈谈c与c++中struct的不同点
c语⾔中struct只有数据 C语言要用struct
c++中的struct不⽌有数据 还有函数 C++可以省略
6、如何理解c++的封装性
封装特性包含两个⽅⾯,⼀个是变量和函数合成⼀个整体,⼀个是给属性和函数增加访问权限
7、谈谈你对c++构造与析构的理解
对象的初始化和清理也是两个⾮常重要的安全问题,⼀个对象或者变量没有初始时,对其使⽤后果是未知,同样的使⽤完⼀个变量,没有及时清理,也会造成⼀定的安全问题。c++为了给我们提供这种问题的解决⽅案,构造函数和析构函数,这两个函数将会被编译器⾃动调⽤,完成对象初始化和对象清理⼯作。
⽆论你是否喜欢,对象的初始化和清理⼯作是编译器强制我们要做的事情,即使你不提供初始化操作和清理操作,编译器也会给你增加默认的操作,只是这个默认初始化操作不会做任何事,所以编写类就应该顺便提供初始化函数。
构造函数主要作⽤在于创建对象时为对象的成员属性赋值,构造函数由编译器⾃动调⽤,⽆须⼿动调⽤。 主要⽤于对象销毁前系统⾃动调⽤,执⾏⼀些清理⼯作。
构造函数和析构函数,这两个函数将会被编译器自动调用,构造函数完成对象的初始化动作,析构函数在对象结束的时候完成清理工作。
注意:对象的初始化和清理工作是编译器强制我们要做的事情,即使你不提供初始化操作和清理操作,编译器也会给你增加默认的操作,只是这个默认初始化操作不会做任何事。
构造函数:实例化对象的时候系统自动调用
析构函数:对象释放的时候系统自动调用
构造函数语法:
构造函数函数名和类名相同,没有返回类型,连void都不可以,但可以有参数,可以重载
析构函数语法:
析构函数函数名是在类名前面加”~”组成,没有返回类型,连void都不可以,不能有参数,不能重载
8、构造函数的分类
按参数类型:分为无参构造函数和有参构造函数
按类型分类:普通构造函数和拷贝构造函数(复制构造函数)
9、构造函数的调⽤规则
默认情况下,c++编译器⾄少为我们写的类增加3个函数 1.默认构造函数(⽆参,函数体为空) 2.默认析构函数(⽆参,函数体为空) 3.默认拷⻉构造函数,对类中⾮静态成员属性简单值拷⻉ 如果⽤户定义拷⻉构造函数,c++不会再提供任何默认构造函数 如果⽤户定义了普通构造(⾮拷⻉),c++不在提供默认⽆参构造,但是会提供默认拷⻉构造
//调用拷贝构造函数(如果用户 不实现拷贝构造 系统将调用默认的拷贝构造)
//默认的拷贝构造:单纯的整体赋值(浅拷贝)
//如果用户实现了 拷贝构造 系统将调用用户实现的拷贝构造
记住一句话:旧对象 初始化 新对象 才会调用拷贝构造函数。
拷贝构造函数的注意事项
1.不能调用拷贝构造函数去初始化匿名对象,
2.对象作为函数的参数 如果实参与形参 都是普通对象 那么就会调用拷贝构造
3.函数返回局部对象 在qt中会被优化 从而调用不了拷贝构造
10、谈谈你对浅拷⻉与深拷⻉的区别
浅拷⻉
同⼀类型的对象之间可以赋值,使得两个对象的成员变量的值相同,两个对象仍然是独⽴的两个对象,这种情况被称为浅拷⻉. ⼀般情况下,浅拷⻉没有任何副作⽤,但是当类中有指针,并且指针指向动态分配的内存空间,析构函数做了动态内存释放的处理,会导致内存问题。 浅拷贝的问题(多次释放同一块堆区空间)
深拷⻉
当类中有指针,并且此指针有动态分配空间,析构函数做了释放处理,往往需要⾃定义拷⻉构造函数,⾃⾏给指针动态分配空间,深拷⻉
如果类中的成员 指向了堆区空间 一定要记得在析构函数中 释放该空间
如果用户 不实现 拷贝构造 系统就会提供默认拷贝构造
而默认拷贝构造 只是单纯的赋值 容易造成浅拷贝问题
用户记得 要实现:无参构造(初始化数据)、有参构造(赋参数)、拷贝构造(深拷贝) 、析构函数(释放空间)
11、谈谈啥叫对象成员以及对象成员的构造函数调⽤⽅式
在类中定义的数据成员⼀般都是基本的数据类型。但是类中的成员也可以是对象,叫做对象成员。
C++中对对象的初始化是⾮常重要的操作,当创建⼀个对象的时候,c++编译器必须确保调⽤了所有⼦对象的构造函数。如果所有的⼦对象有默认构造函数,编译器可以⾃动调⽤他们。但是如果⼦对象没有默认的构造函数,或者想指定调⽤某个构造函数怎么办?
那么是否可以在类的构造函数直接调⽤⼦类的属性完成初始化呢?但是如果⼦类的成员属性是私有的,我们是没有办法访问并完成初始化的。
解决办法⾮常简单:对于⼦类调⽤构造函数,c++为此提供了专⻔的语法,即构造函数初始化列表。 当调⽤构造函数时,⾸先按各对象成员在类定义中的顺序(和参数列表的顺序⽆关)依次调⽤它们的构造函数,对这些对象初始化,最后再调⽤本身的函数体。也就是说,先调⽤对象成员的构造函数,再调⽤本身的构造函数。 析构函数和构造函数调⽤顺序相反,先构造,后析构。
12、谈谈你对explicit的理解
c++提供了关键字explicit,禁⽌通过构造函数进⾏的隐式转换。声明为explicit的构造函数不能在隐式转换中使⽤
(1)C++中只带有一个参数的构造函数,或者或者除了第一个参数外其余参数都有缺省值的多参构造函数,承担了两个角色:
1.用于构建单参数的类对象.
2.隐含的类型转换操作符.
例如:一个类A的构造函数A(int i)就是,既可以用来作为构造器,又可以实现隐式转换A a=1;因为1可以通过构造函数A(int i)转换为一个类A的对象。(隐含的类型转换操作符)
(2)explicit关键字只对有一个参数的类构造函数有效, 如果类构造函数参数大于或等于两个时, 是不会产生隐式转换的, 所以explicit关键字也就无效了
(3)声明为explicit的构造函数不能在隐式转换中使用,只能显示调用,去构造一个类对象
(4)尽量避免有二义性的类型转换,如果类中包含一个或多个隐式类型转换,则必需使用explicit关键字确保在类类型和目标类型之间只存在唯一一种隐式转换方式,否则将出现二义性。
(5)但是将拷贝构造函数声明成explicit并不是良好的设计,一般只将有单个参数的constructor声明为explicit,而copy constructor不要声明为explicit.
- 谈谈c中malloc free 与 c++中的new delete有啥区别,或则c++在堆区申请对象时为啥推荐使⽤new delete
c++中的malloc free的问题:
1、程序员必须确定对象的⻓度
2、malloc返回⼀个void指针,c++不允许将void赋值给其他任何指针,必须强转
3、malloc可能申请内存失败,所以必须判断返回值来确保内存分配成功
4、malloc 不会调用构造函数。free 不会调用析构函数(重要的)
5、⽤户在使⽤对象之前必须记住对他初始化,构造函数不能显示调⽤初始化(构造函数是由编译器调⽤),⽤户有可能忘记调⽤初始化函数
总结:c的动态内存分配函数太复杂,容易令⼈混淆,是不可接受的,c++中我们推荐使⽤运算符new 和 delete (1)new操作符能确定在调⽤构造函数初始化之前内存分配是成功的,所有不⽤显式确定调⽤是否成功 (2)delete表达式先调⽤析构函数,然后释放内存
- 谈谈你对static静态成员变量的理解
成员:成员变量 成员函数
static 修饰成员变量 修饰成员函数
static声明为静态的,称为静态成员。 不管这个类创建了多少个对象,静态成员只有一个拷贝,这个拷贝被所有属于这个类的对象共享。
静态成员 属于类 而不是对象。
在⼀个类中,若将⼀个成员变量声明为static,这种成员称为静态成员变量。与⼀般的数据成员不同, ⽆论建⽴了多少个对象,都只有⼀个静态数据的拷⻉。静态成员变量,属于某个类,所有对象共享。 静态变量,是在编译阶段就分配空间,对象还没有创建时,就已经分配空间
注意:
- 静态成员变量必须在类中声明,在类外定义。 2、静态数据成员不属于某个对象,在为对象分配空 间中不包括静态成员所占空间。 3、静态数据成员可以通过类名或者对象名来引⽤
4、普通成员变量 属于对象的 只能通过对象名访问
5、静态成员变量 是属于类 可以通过类名称::直接访问
- 谈谈你对static静态成员函数的理解
普通成员函数 依赖于对象的 必须对象调用
在类定义中,前⾯有static说明的成员函数称为静态成员函数。静态成员函数使⽤⽅式和静态变量⼀样,同样在对象没有创建前,即可通过类名调⽤。静态成员函数主要为了访问静态变量,但是,不能访问普通成员变量。
静态成员函数的意义,不在于信息共享,数据沟通,⽽在于管理静态数据成员,完成对静态数据成员的 封装。
- 静态成员函数只能访问静态变量,不能访问普通成员变量
- 静态成员函数的使⽤和静态成员变量⼀样
- 静态成员函数也有访问权限
- 普通成员函数可访问静态成员变量、也可以访问⾮静态成员变量st。
5、如果一个类的成员,既要实现共享,又要实现不可改变,那就用 atic const 修饰。
16、谈谈你对this的理解
成员函数通过this指针即可知道操作的是那个对象的数据。This指针是⼀种隐含指针,它隐含于每个类的⾮静态成员函数中。This指针⽆需定义,直接使⽤即可。
注意:静态成员函数内部没有this指针,静态成员函数不能操作⾮静态成员变量
1、this指针是隐含在对象成员函数内的一种指针
2、成员函数通过this指针即可知道操作的是哪个对象的数据
3、静态成员函数内部没有this指针,静态成员函数不能操作非静态成员变量
(静态成员函数 是属于类 函数内部 没有this指针)
17、谈谈你对友元的理解
类的主要特点之⼀是数据隐藏,即类的私有成员⽆法在类的外部(作⽤域之外)访问。但是,有时候需要在类的外部访问类的私有成员,怎么办?
解决⽅法是使⽤友元函数,友元函数是⼀种特权函数,c++允许这个特权函数访问私有成员
- friend关键字只出现在声明处
- 其他类、类成员函数、全局函数都可声明为友元
- 友元函数不是类的成员,不带this指针
- 友元函数可访问对象任意成员属性,包括私有属性
友元的注意事项:
- 友元关系不能被继承。
- 友元关系是单向的,类A是类B的朋友,但类B不⼀定是类A的朋友。
- 友元关系不具有传递性。类B是类A的朋友,类C是类B的朋友,但类C不⼀定是类A的朋友。
18、谈谈你对继承的理解
c++最重要的特征是代码重⽤,通过继承机制可以利⽤已有的数据类型来定义新的数据类型,新的类不仅拥有旧类的成员,还拥有新定义的成员。
⼀个B类继承于A类,或称从类A派⽣类B。这样的话,类A成为基类(⽗类),类B成为派⽣类(⼦类)。
派⽣类中的成员,包含两⼤部分:
- ⼀类是从基类继承过来的,⼀类是⾃⼰增加的成员。
- 从基类继承过过来的表现其共性,⽽新增的成员体现了其个性
19、谈谈继承中的构造与析构的顺序
1、⼦类对象在创建时会⾸先调⽤⽗类的构造函数
2、⽗类构造函数执⾏完毕后,才会调⽤⼦类的构造函数
3、当⽗类构造函数有参数时,需要在⼦类初始化列表(参数列表)中显示调⽤⽗类构造函数构函数调⽤顺序和构造函数相反
构造的顺序:基类->派生类
析构的顺序:派生类->基类
20、继承中同名成员的处理⽅法
1、当⼦类成员和⽗类成员同名时,⼦类依然从⽗类继承同名成员
2、如果⼦类有成员和⽗类同名,⼦类访问其成员默认访问⼦类的成员(本作⽤域,就近原则)
3、在⼦类通过作⽤域::进⾏同名成员区分(在派⽣类中使⽤基类的同名成员,显示使⽤类名限定符)
4、如果在子类中必须使用父类的同名成员,必须加父类的作用域。
21、哪些函数是⽆法继承的
所有的函数都能⾃动从基类继承到派⽣类中。
构造函数和析构函数⽤来处理对象的创建和析构操作,构造和析构函数只知道对它们的特定层次的对象做什么,也就是说构造函数和析构函数不能被继承,必须为每⼀个特定的派⽣类分别创建。
另外operator=也不能被继承,因为它完成类似构造函数的⾏为。也就是说尽管我们知道如何⽤=右边的对象如何初始化=左边的对象的所有成员,但是这个并不意味着对其派⽣类依然有效。
在继承的过程中,如果没有创建这些函数,编译器会⾃动⽣成它们。
(1)父类中的public数据 在子类中 也是public
父类中的private数据 在子类中 是不可见的
父类中的protected数据 在子类中 是protected的
(public 继承 父类中的私有数据 在子类 不可见 其他保持原样)
(2)父类中的public数据 在子类中 也是protected
父类中的private数据 在子类中 是不可见的
父类中的protected数据 在子类中 是protected的
(保护继承 父类的私有数据 在子类中 不可见 其他数据 都变保护)
(3)父类中的public数据 在子类中 也是private
父类中的private数据 在子类中 是不可见的
父类中的protected数据 在子类中 是private的
(私有继承 父类中的私有数据在子类中 不可见 其他变成私有)
22、静态多态与动态多态的区别
静态多态和动态多态的区别就是函数地址是早绑定(静态联编)还是晚绑定(动态联编)。如果函数的调⽤, 在编译阶段就可以确定函数的调⽤地址,并产⽣代码,就是静态多态(编译时多态),就是说地址是早绑定的。⽽如果函数的调⽤地址不能编译不能在编译期间确定,⽽需要在运⾏时才能决定,这这就属于晚绑定(动态多态,运⾏时多态)
多态的分类:静态多态(静态联编)、动态多态(动态联编)
静态多态(静态联编):函数入口地址 是在 编译阶段 确定(运算符重载、函数重载)
动态多态(动态联编):函数入口地址 是在 运行阶段 确定(虚函数)
23、c++的动态捆绑机制是怎样的?
⾸先,我们看看编译器如何处理虚函数。
当编译器发现我们的类中有虚函数的时候,编译器会创建⼀张虚函数表,把虚函数的函数⼊⼝地址放到虚函数表中,并且在类中秘密增加⼀个指针,这个指针就是vpointer(缩写vptr),这个指针是指向对象的虚函数表。在多态调⽤的时候,根据vptr指针,找到虚函数表来实现动态绑定
当虚函数涉及到继承的时候 子类 会继承 父类的(虚函数指针vfptr 虚函数表vftable),编译器会将虚函数表中的函数入口地址 更新 成子类的 同名(返回值、参数都相同)的函数入口地址。
如果基类指针、引用 访问虚函数的时候 就会 间接的调用 子类的虚函数.
使用基类指针、引用 访问 子类对象中的成员方法(虚函数)
24、多态成⽴的条件
1、有继承
2、⼦类重写⽗类虚函数
a) 返回值,函数名字,函数参数,必须和⽗类完全⼀致(析构函 数除外)
b) ⼦类中virtual关键字可写可不写,建议写
3、类型兼容,⽗类指针指向⼦类对象
25、虚析构的作⽤
虚析构函数是为了解决基类的指针指向派⽣类对象,并⽤基类的指针删除派⽣类对象
虚析构作用:通过基类指针、引用 释放 子类的所有空间。
虚析构:在虚析构函数前加virtual修饰
- 纯虚析构与⾮纯虚析构的区别
纯虚析构函数在c++中是合法的,但是在使⽤的时候有⼀个额外的限制:必须为纯虚析构函数提供⼀个函数体。 (原因:通过基类指针 释放子类对象时 先调用子类析构 再父类析构)
纯虚析构函数和⾮纯析构函数之间唯⼀的不同之处在于纯虚析构函数使得基类是抽象类,不能创建基类的对象。
注意:如果类的⽬的不是为了实现多态,作为基类来使⽤,就不要声明虚析构函数,反之,则应该为类声明虚析构函数
知识点【虚函数 纯虚函数 虚析构 纯虚析构】(重要)
1、虚函数:只是virtual修饰有函数体 (作用于成员函数)
目的:通过基类指针或引用 操作 子类的方法
2、纯虚函数:virtual修饰 加=0 没有函数 所在的类为抽象类
目的:为子类提供固定的流程和接口
3、虚析构:virtual修饰 类中的析构函数
目的:为了解决基:类的指针指向派生类对象,并用基类的指针删除派生类对 象
4、纯虚析构virtual修饰 加=0 必须实现析构的函数体
目的:用基类的指针删除派生类对象、同时提供固定接口
虚析构与纯虚析构共性:
1.都可以解决父类指针释放子类对象时无法释放干净的问题
2.都需要有具体的函数实现(代码实现) (如果纯虚析构只有声明没有实现会报错 :“无法解析的外部命令”
区别:
如果类中有了纯虚析构之后, 这个类也属于抽象类, 无法实例化对象
如果只写了虚析构, 但是没有纯虚析构和纯虚函数, 此时还是可以实例化对象的
总结:
1.虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
2.如果子类中没有堆区数据,可以不写虚析构或纯虚析构
3.拥有纯虚析构函数的类也属于抽象类(无法实例化对象)
27、谈谈重载、重写、重定义的概念
1、重载,同⼀作⽤域的同名函数 a、同⼀个作⽤域 b、参数个数,参数顺序,参数类型不同 c、和函数返回值,没有关系 d、const也可以作为重载条件 //do(const Teacher& t){} do(Teacher& t)
int fun(int a){}
int fun(int b,int c){}
int fun(char b,int c){}
- 重定义 (隐藏) a、有继承 b、⼦类(派⽣类)重新定义⽗类(基类)的同名成员(⾮virtual函数)
class Base
{ public: void fun(int){}
void fun(int,int){} }
class Son:public Base{ public: void fun(参数可以不同){}//重定义 }
3、重写 (覆盖) a、有继承 b、⼦类(派⽣类)重写⽗类(基类)的virtual函数 c、函数返回值,函数名字,函数参数,必须和基类中的虚函数⼀致
class Base{ public: virtual void fun(int){} }
class Son:public Base{ public: virtual void fun(int){}//重写 }
28、谈谈你对模板的理解
所谓函数模板,实际上是建⽴⼀个通⽤函数,其函数类型和形参类型不具体制定,⽤⼀个虚拟的类型来代表。这个通⽤函数就称为函数模板。
c++提供两种模板机制:函数模板和类模板
模板把函数或类要处理的数据类型参数化,表现为参数的多态性,成为类属.
模板⽤于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通⽤⾏为
c++提供了函数模板。
所谓的函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体化,用一个虚拟的类型来代替。这个通用函数,就称为函数模板
凡是函数体相同的函数都可以用这个模板代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时,系统会根据实参的类型来取代模板中的虚拟类型,从而实现不同函数的功能。
- 函数模板和普通函数的区别
//函数模板和普通函数 都识别 优先选择普通函数
//函数模板和普通函数 都识别 选择函数模板 加<>
函数模板不允许⾃动类型转换
普通函数可以⾃动实现类型转换
30、函数模板和普通函数同时出现时的调⽤机制
1、c++编译器优先考虑普通函数
2、可以通过空模板实参列表的语法限定编译器只能通过模板匹配
3、函数模板可以像普通函数那样可以被重载
4、如果函数模板可以产⽣⼀个更好的匹配,那么选择模板
31、谈谈静态转换(static_cast)、动态转换(dynamic_cast)、常量转换(const_cast)、重新解析转换(reinterpret_cast)的区别
静态转换( static_cast )
⽤于类层次结构中基类(⽗类)和派⽣类(⼦类)之间指针或引⽤的转换。
- 进⾏上⾏转换(把派⽣类的指针或引⽤转换成基类表示)是安全的;
- 进⾏下⾏转换(把基类指针或引⽤转换成派⽣类表示)时,由于没有动态类型检查,所以是不安全的。
3、⽤于基本数据类型之间的转换,如把int转换成char,把char转换成int。这种转换的安全性也要开发⼈员来保证
动态转换( dynamic_cast )
- dynamic_cast主要⽤于类层次间的上⾏转换和下⾏转换;
2、在类层次间进⾏上⾏转换时,dynamic_cast和static_cast的效果是⼀样的;
3、在进⾏下⾏转换时,dynamic_cast具有类型检查的功 能,⽐static_cast更安全
常量转换( const_cast )
- 该运算符⽤来修改类型的const属性。
- 常量指针被转化成⾮常量指针,并且仍然指向原来的对象;
3、常量引⽤被转换成⾮常量引⽤,并且仍然指向原来的对象;
注意:不能直接对⾮指针和⾮引⽤的变量使⽤const_cast操作符去直接移除它的const.
重新解释转换( reinterpret_cast )
这是最不安全的⼀种转换机制,最有可能出问题。 主要⽤于将⼀种数据类型从⼀种类型转换为另⼀种类型。它可以将⼀个指针转换成⼀个整数,也可以将⼀个整数转换成⼀个指针
32、谈谈你对异常的理解
异常处理就是处理程序中的错误。所谓错误是指在程序运⾏的过程中发⽣的⼀些异常事件(如:除0溢出,数组下标越界,所要读取的⽂件不存在,空指针,内存不⾜等等)
常见的异常:除0溢出,数组下标越界,所要读取的文件不存在,空指针,内存不足等等。
c++的异常一旦抛出 如果不捕获该异常 程序直接退出
33、谈谈c++的异常机制相⽐c语⾔的异常处理的优势
1、函数的返回值可以忽略,但异常不可忽略。如果程序出现异常,但是没有被捕获,程序就会终⽌,这多少会促使程序员开发出来的程序更健壮⼀点。⽽如果使⽤C语⾔的error宏或者函数返回值,调⽤者都有可能忘记检查,从⽽没有对错误进⾏处理,结果造成程序莫名其⾯的终⽌或出现错误的结果。
2、整型返回值没有任何语义信息。⽽异常却包含语义信息,有时你从类名就能够体现出来。
3、整型返回值缺乏相关的上下⽂信息。异常作为⼀个类,可以拥有⾃⼰的成员,这些成员就可以传递⾜够的信息。
4、异常处理可以在调⽤跳级。这是⼀个代码编写时的问题:假设在有多个函数的调⽤栈中出现了某个错误,使⽤整型返回码要求你在每⼀级函数中都要进⾏处理。⽽使⽤异常处理的栈展开机制,只需要在⼀处进⾏处理就可以了,不需要每级函数都处理。
(1)C语言通过返回值 来判断 第一:容易忽略 第二:容易和正常的结果混淆
(2)整型返回值没有任何语义信息。⽽异常却包含语义信息
(3)整型返回值缺乏相关的上下⽂信息。异常作为⼀个类,可以拥有⾃⼰的成员,这些成员就可以传递⾜够的信息。
C语⾔篇
1、描述⼀下gcc的编译过程?
gcc编译过程分为4个阶段:预处理、编译、汇编、链接。
预处理:头⽂件包含、宏替换、条件编译、删除注释
编译:主要进⾏词法、语法、语义分析等,检查⽆误后将预处理好的⽂件编译成汇编⽂件。
汇编:将汇编⽂件转换成 ⼆进制⽬标⽂件
链接:将项⽬中的各个⼆进制⽂件+所需的库+启动代码链接成可执⾏⽂件
2、内存的最⼩存储单位以及内存的最⼩计量单位分别是?
内存的最⼩存储单位为 ⼆进制位, 内存的最⼩计量单位 字节
3、#include<> 与#include ""的区别?
include<>到系统指定⽬录寻找头⽂件,#include ""先到项⽬所在⽬录寻找头⽂件,如果没有找再到系 统指定的⽬录下寻找
4、描述⼀下变量的命名规则
变量名有字⺟、数值、下划线组成,但不能以数值开头
5、变量的声明与定义有啥区别?
声明变量 不需要 建⽴存储空间, 变量的定义需要建⽴存储空间
6、谈谈c语⾔中有符号和⽆符号的区别?
有符号:数据的最⾼位为符号位,0表示正数,1表示负数
⽆符号:数据的最⾼位不是符号位,⽽是数据的⼀部分
7、谈谈计算机中补码的意义
统⼀了零的编码
将符号位与其他位统⼀处理
将减法运算转换成加法运算
8、谈谈数组的特点
同⼀个数组所有的成员都是相同的数据类型,同时所有的成员在内存中的地址是连续的
9、数组的分类
数组的分类主要是:静态数组、动态数组两类。
静态数组:类似int arr[5];在程序运⾏就确定了数组的⼤⼩,运⾏过程不能更改数组的⼤⼩。
动态数组:主要是在堆区申请的空间,数组的⼤⼩是在程序运⾏过程中确定,可以更改数组的⼤⼩。
10、描述⼀下⼀维数组的不初始化、部分初始化、完全初始化的不同点
不初始化:如果是局部数组 数组元素的内容随机 如果是全局数组,数组的元素内容为0
部分初始化:未被初始化的部分⾃动补0
完全初始化:如果⼀个数组全部初始化 可以省略元素的个数 数组的⼤⼩由初始化的个数确定
11、谈谈数组名作为类型、作为地址、对数组名取地址的区别?
数组名作为类型:代表的是整个数组的⼤⼩
数组名作为地址:代表的是数组⾸元素的地址
对数组名取地址:代表的是数组的⾸地址
12、谈谈你对⼆维数组在物理上以及逻辑上的数组维度理解
⼆维数组在逻辑上是⼆维的,在物理上是⼀维的
13、描述⼀下函数的定义与函数的声明的区别
函数定义:是指对函数功能的确⽴,包括指定函数名、函数类型、形参及其类型、函数体等,它是⼀个完整的、独⽴的函数单位。
函数的声明:是把函数的名字、函数类型以及形参的个数、类型和顺序(注意,不包括函数体)通知编译系统,以便在对包含函数调⽤的语句进⾏编译时,据此对其进⾏对照检查(例如函数名是否正确,实参与形参的类型和个数是否⼀致)
- 描述⼀下指针与指针变量的区别指针:
内存中每⼀个字节都会分配⼀个32位或64位的编号,这个编号就是地址, ⽽指针就是内存单元的
编号。
指针变量:本质是变量 只是该变量存放的是空间的地址编号
15、描述⼀下32位或64位平台下指针的⼤⼩
32位平台:任意类型的指针⼤⼩为4字节
64位平台:任意类型的指针⼤⼩为8字节
16、描述⼀下指针数组的概念
指针数组本质是数组,只是数组的每个元素是⼀个指针(地址)
17、描述⼀下普通局部变量、普通全局变量、静态局部变量、静态全局变量的区别
普通局部变量:
存在栈区、不初始化内容随机、只在定义所在的复合语句中有效、符合语句结束变量空间释放
普通全局变量
存在全局区、不初始化内容为0、进程结束空间才被释放,能被当前源⽂件或其他源⽂件使⽤,只是其他源⽂件使⽤的时候,记得使⽤extern修饰
静态局部变量:
存在全局区、不初始化内容为0、整个进程结束空间才被释放,只能在定义所在的复合语句中有效
静态全局变量
存在全局区、不初始化内容为0、整个进程结束空间才被释放,只能被当前源⽂件使⽤
18、描述⼀下内存分区
程序在运⾏前:分为代码区、BSS段(未初始化数据区)、data段(初始化数据区)
程序在运⾏后:堆区、栈区、全局区(静态区)、⽂字常量区、代码区
19、在使⽤realloc给已分配的堆区空间追加空间时需要注意啥?
记得⽤指针变量保存realloc的返回值
20、结构体与共⽤体的区别是啥
结构体中的成员拥有独⽴的空间,共⽤体的成员共享同⼀块空间,但是每个共⽤体成员能访问共⽤区的空间⼤⼩是由成员⾃身的类型决定
- 谈谈⽂件的分类
⽂件分为⼆进制和⽂本⽂件
⼆进制⽂件基于值编码,需要根据具体的应⽤才能知道某个值具体的含义
⽂本⽂件基于字符编码,⼀个字节⼀个意思,可以通过记事本打开
22、⽂件缓冲区刷新⽅式有⼏种
⾏刷新、满刷新、强制刷新、关闭刷新
23、哪些情况下会出现野指针
指针变量未初始化、指针释放后未为置空、指针操作超越变量作⽤域
24、如何理解指针作为函数参数的输⼊和输出特性
输⼊特性:主调函数分配空间 背调函数使⽤该空间
输出特性:被调⽤分配空间 主调函数使⽤该空间
25、如何理解结构体的浅拷⻉与深拷⻉
当结构体中有指针成员的时候容易出现浅拷⻉与深拷⻉的问题。
浅拷⻉就是,两个结构体变量的指针成员指向同⼀块堆区空间,在各个结构体变量释放的时候会出现多 次释放同⼀段堆区空间
深拷⻉就是,让两个结构体变量的指针成员分别指向不同的堆区空间,只是空间内容拷⻉⼀份,这样在各个结构体变量释放的时候就不会出现多次释放同⼀段堆区空间的问题
26、描述⼀下结构体对⻬规则
1. 结构体成员对⻬规则。第⼀个结构体成员应该放在offffset(偏移量)为0的地⽅,以后每个数组成员应该放在offffset 为min(当前成员的⼤⼩,#pargama pack(n))整数倍的地⽅开始(⽐如int在32位机器为4字节,#pargama pack(2),那么从2的倍数地⽅开始存储)。
2. 结构体总的⼤⼩,也就是sizeof的结果,必须是min(结构体内部最⼤成员,#pargama pack(n))
的整数倍,不⾜要补⻬。
3. 结构体做为成员的对⻬规则。如果⼀个结构体B⾥嵌套另⼀个结构体A,还是以最⼤成员类型的⼤⼩ 对⻬,但是结构体A的起点为A内部最⼤成员的整数倍的地⽅。(struct B⾥存有struct A,A⾥有
char,int,double等成员,那A应该从8的整数倍开始存储。),结构体A中的成员的对⻬规则仍满⾜原则1、原则2。
- 啥叫宏函数以及作⽤
在项⽬中,经常把⼀些短⼩⽽⼜频繁使⽤的函数写成宏函数,这是由于宏函数没有普通函数参数压栈、 跳转、返回等的开销,可以调⾼程序的效率。 宏通过使⽤参数,可以创建外形和作⽤都与函数类似地类函数宏(function-like macro). 宏的参数也⽤圆括号括起来,来保证宏函数的完整性。
28、如何理解库函数
库是已经写好的、成熟的、可复⽤的代码。每个程序都需要依赖很多底层库,不可能每个⼈的代码从零开始编写代码,因此库的存在具有⾮常重要的意义。 在我们的开发的应⽤中经常有⼀些公共代码是需要反复使⽤的,就把这些代码编译为库⽂件。 库可以简单看成⼀组⽬标⽂件的集合,将这些⽬标⽂件经过压缩打包之后形成的⼀个⽂件。像在Windows这样的平台上,最常⽤的c语⾔库是由集成按开发环境所附带的运⾏库,这些库⼀般由编译⼚商提供
物联网篇:
1、RFID了解多少?
⽤过13.56M频段的,对国产的FM17550和恩智普的RC522都⽐较熟悉,能够实现对A类卡的读写充
值、扣款等操作以及密码的认证和修改,其它频段⼯作原理相似,主要参照读写器数据⼿册和《中国⾦融集成电路(IC)卡规范》对设备进⾏操作。
2、请问s50卡的默认密码是多少?
s50卡密码分为A和B两种,都是6个字节,默认都是0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
3、请问zigbee是⽤的什么平台?
我们⽤的是TI公司出的CC2530平台,功耗和成本都⽐较低。
4、你们使⽤zigbee模块是基于AT指令模式,还是协议栈模式?
我们是基于TI提供的协议栈接⼝直接开发的。
5、zigbee的物理信道有⼏个?
zigbee可以⼯作在3个频段,总共27个物理信道上,分别是868.3M的1个信道,和906M的10个信道,还有2.4G的16个信道。
6、zigbee协议栈规定了模块的⼏种通信⻆⾊?
三种:全功能节点的协调器,全功能节点的路由器,半功能节点的终端。
7、MQTT是什么?
MQTT是IBM推出的⼀种针对移动终端设备的基于TCP/IP的发布/订阅协议,实现了对通信双⽅在时间、 空间、同步这三种场合的解耦。
8、你们⽤的是MQTT那个版本?
我们⽬前⽤的是mosquitto开源软件,⽀持MQTTv3.1版本。
9、消息订阅发布可以使⽤通配符,请问具体怎么使⽤有什么需要注意的?
+代表单层过滤,#代表末尾多层过滤,但通配符只适⽤于订阅主题,⽽不适⽤于发布主题
10、NB-IOT是什么?
NB-IoT是指Narrow Band Internet of Things,聚焦于低功耗窄带⼴域物联⽹,⼯作在License频谱,能与现有移动通信⽹共存。
11、NB-IOT主要解决了什么问题?
主要解决了⼴域⽹通信,⾼资费、⼤功耗等问题。
12、你⽤的是什么NB-IOT模块,都⽀持哪些协议?
我们⽤过上海移远BC系列模块,还⽤过中移物联的m5310a模块,基本上都是全⽹通,并⽀持协议有:IPv4、IPV6、UDP、TCP、CoAP、LwM2M、Non-IP、DTLS、MQTT等。
BS开发篇
1、什么是B/S架构
Browser/Server(浏览器/服务器结构),是随着 Internet 技术的兴起,是对 C/S 结构的⼀种变化或者改进的结构。 随着 Windows98/Windows2000 将浏览器技术植⼊操作系统内部,这种结构更成为当今应⽤软件的⾸选体系结构
2、B/S 架构 与 C/S 架构对⽐
C/S
专⽤⽹络 ⾯向相对固定的⽤户群、信息安全的控制能⼒很强 更加注重流程、系统运⾏速度可较少考虑 升级难 处理问题集中 与操作系统关系密切 交互性低
B/S
⼴域⽹ ⾯向是不可知的⽤户群、对安全的控制能⼒相对弱 对安全以及访问速度要多重的考虑 B/S 结构的程序架构是发展的趋势 开销⼩、⽅便升级 处理问题分散 跨平台,与浏览器相关 交互密集
3、什么是Javascript
Javascript 是⼀种基于对象并具有安全性能的脚本语⾔,是由浏览器内解释器翻译成可执⾏格式后执⾏,在概念和设计⽅⾯,Java 和 Javascript 是两种完全不同的语⾔。 Javascript 的四个特点:基于对象的语⾔、简单性、动态性、跨平台性
- 描述⼀下ajax⼯作流程
1、创建对象 2、设置回调函数 ,fun 函数 3、open 创建服务器请求 4、send 向服务器发送请求, 5、服务器有结果会⾃动调⽤ fun
数据库篇
1、谈谈你对数据库中事务的理解
事务(Transaction)可以使⽤ BEGIN TRANSACTION 命令或简单的 BEGIN 命令来启动。此类事务通常会持续执⾏下去,直到遇到下⼀个 COMMIT 或 ROLLBACK 命令。不过在数据库关闭或发⽣错误时,事务 处理也会回滚.
在 SQLite 中,默认情况下,每条 SQL 语句自成事务。
begin:开始一个事务,之后的所有操作都可以取消
commit:使 begin 后的所有命令得到确认。
rollback:取消 begin 后的所有操作。
2、谈谈你对数据库中联结表的理解
保存数据时往往不会将所有数据保存在⼀个表中,⽽是在多个表中存储,联结表就是从多个表中查询数据。 在⼀个表中不利于分解数据,也容易使相同数据出现多次,浪费存储空间;使⽤联结表查看各个数据更直观, 这使得在处理数据时更简单。
3、谈谈你对数据库中触发器的理解
SQLite 的触发器是数据库的回调函数,它会在指定的数据库事件发⽣时⾃动执⾏调⽤
4、谈谈你对数据库中索引的理解
索引是对数据库表中⼀列或多列的值进⾏排序的⼀种结构,使⽤索引可快速访问数据库表中的特定信息。索引就是恰当的排序,经过某种算法优化,使查找次数要少的多的多
缺点: 1、索引数据可能要占⽤⼤量的存储空间,因此并⾮所有数据都适合索引
2、索引改善检索操作的性能,但降低了数据插⼊、修改和删除的性能
5、谈谈你对数据库中主键、唯⼀约束、检查约束的理解
主键: 惟⼀的标识⼀⾏(⼀张表中只能有⼀个主键) 主键应当是对⽤户没有意义的(常⽤于索引) 永远不要更新主键,否则违反对⽤户没有意义原则 主键不应包含动态变化的数据,如时间戳、创建时间列、修改时间列等主键应当有计算机⾃动⽣成(保证唯⼀性)
唯⼀约束: ⽤来保证⼀个列(或⼀组列)中数据唯⼀,类似于主键,但跟主键有区别 表可包含多个唯⼀约束,但只允许⼀个主键 唯⼀约束列可修改或更新
检查约束: ⽤来保证⼀个列(或⼀组列)中的数据满⾜⼀组指定的条件。 指定范围,检查最⼤或最⼩范围, 通过 check实现。
主键:primary key 手动设置主键,每个表只能有一个主键,设置为主键的列数据不可以重复。
唯⼀约束: ⽤来保证⼀个列(或⼀组列)中数据唯⼀,
检查约束: ⽤来保证⼀个列(或⼀组列)中的数据满⾜⼀组指定的条件。
STL标准模板库篇
1、谈谈STL的六⼤组件
容器:各种数据结构,如vector、list、deque、set、map等,⽤来存放数据,从实现⻆度来看,STL容器是⼀种class template。
算法:各种常⽤的算法,如sort、fifind、copy、for_each。从实现的⻆度来看,STL算法是⼀种function tempalte.
迭代器:扮演了容器与算法之间的胶合剂,共有五种类型,从实现⻆度来看,迭代器是⼀种将
operator* , operator-> , operator++,operator--等指针相关操作予以重载的class template. 所有STL容器都附带有⾃⼰专属的迭代器,只有容器的设计者才知道如何遍历⾃⼰的元素。原⽣指针(native pointer)也是⼀种迭代器。
仿函数:⾏为类似函数,可作为算法的某种策略。从实现⻆度来看,仿函数是⼀种重载了operator()的class 或者class template
适配器:⼀种⽤来修饰容器或者仿函数或迭代器接⼝的东⻄。
空间配置:负责空间的配置与管理。从实现⻆度看,配置器是⼀个实现了动态空间配置、空间管理、空间释放的class tempalte.
2、谈谈STL六⼤主键的关系
STL六⼤组件的交互关系,容器通过空间配置器取得数据存储空间,算法通过迭代器存储容器中的内容,适配器可以修饰仿函数,仿函数可以协助算法完成不同的策略的变化.
3、vector容器相⽐于普通的数组array有啥优点
vector的数据安排以及操作⽅式,与array⾮常相似,两者的唯⼀差别在于空间的运⽤的灵活性。Array 是静态空间,⼀旦配置了就不能改变,要换⼤⼀点或者⼩⼀点的空间,可以,⼀切琐碎得由⾃⼰来,⾸ 先配置⼀块新的空间,然后将旧空间的数据搬往新空间,再释放原来的空间。
Vector是动态空间,随着元素的加⼊,它的内部机制会⾃动扩充空间以容纳新元素。因此vector的运⽤对于内存的合理利⽤与运⽤的灵活性有很⼤的帮助,我们再也不必害怕空间不⾜⽽⼀开始就要求⼀个⼤块头的array了
Vector的实现技术,关键在于其对⼤⼩的控制以及重新配置时的数据移动效率,⼀旦vector旧空间满了,如果客户每新增⼀个元素,vector内部只是扩充⼀个元素的空间,实为不智,因为所谓的扩充空间 (不论多⼤),⼀如刚所说,是”配置新空间-数据移动-释放旧空间”的⼤⼯程,时间成本很⾼,应该加⼊某种未⾬绸缪的考虑,稍后我们便可以看到vector的空间配置策略。
4、deque容器(双端动态数组)相对于vector容器(单端动态数组)的区别
Deque容器和vector容器最⼤的差异
a、deque允许使⽤常数项时间对头端进⾏元素的插⼊和删除操作。
b、deque容器没有容量的概念 以分段连续空间组成。
c、Deque是由一段一段的定量的连续空间构成。
queue容器:没有迭代器 不具备遍历功能 只能通过front、back访问
vector容器的迭代器 :单端动态数组
随机访问迭代器 在尾部插入数据
随机访问迭代器:迭代器+n 可以通过编译 就是随机访问迭代器
5、谈谈stack容器的概念 (栈容器)
Stack所有元素的进出都必须符合”先进后出”的条件,只有stack顶端的元素,才有机会被外界取⽤。 Stack不提供遍历功能,也不提供迭代器
6、谈谈queue容器的概念 (队列容器)
Queue是⼀种先进先出(First In First Out,FIFO)的数据结构,它有两个出⼝,queue容器允许从⼀端新增元素,从另⼀端移除元素
Queue所有元素的进出都必须符合”先进先出”的条件,只有queue的顶端元素,才有机会被外界取⽤。
Queue不提供遍历功能,也不提供迭代器
7、谈谈list容器的概念
相较于vector的连续线性空间,list就显得负责许多,它的好处是每次插⼊或者删除⼀个元素,就是配置或者释放⼀个元素的空间。因此,list对于空间的运⽤有绝对的精准,⼀点也不浪费。⽽且,对于任何位置的元素插⼊或元素的移除,list永远是常数时间
- 采⽤动态存储分配,不会造成内存浪费和溢出
- 链表执⾏插⼊和删除操作⼗分⽅便,修改指针即可,不需要移动⼤量元素
- 链表灵活,但是空间和时间额外耗费较⼤
8、谈谈set/multiset容器的概念
Set的特性是。所有元素都会根据元素的键值⾃动被排序。Set的元素不像map那样可以同时拥有实值和键值,set的元素即是键值⼜是实值。Set不允许两个元素有相同的键值
multiset特性及⽤法和set完全相同,唯⼀的差别在于它允许键值重复。set和multiset的底层实现是红⿊ 树,红⿊树为平衡⼆叉树的⼀种
9、谈谈map/multimap容器的概念
Map的特性是,所有元素都会根据元素的键值⾃动排序。Map所有的元素都是pair,同时拥有实值和键值,pair的第⼀元素被视为键值,第⼆元素被视为实值,map不允许两个元素有相同的键值
Multimap和map的操作类似,唯⼀区别multimap键值可重复。
Map和multimap都是以红⿊树为底层实现机制
10、谈谈什么是函数对象
重载函数调⽤操作符的类,其对象常称为函数对象(function object),即它们是⾏为类似函数的对象,也叫仿函数(functor),其实就是重载“()”操作符,使得类对象可以像函数那样调⽤注意:
1、函数对象(仿函数)是⼀个类,不是⼀个函数。 2、函数对象(仿函数)重载了”() ”操作符使得它可以像函数⼀样调⽤
总结:
- 函数对象通常不定义构造函数和析构函数,所以在构造和析构时不会发⽣任何问题,避免了函数调⽤的运⾏时问题。
- 函数对象超出普通函数的概念,函数对象可以有⾃⼰的状态
- 函数对象可内联编译,性能好。⽤函数指针⼏乎不可能 4、模版函数对象使函数对象具有通⽤性,这也是它的优势之⼀
11、谈谈什么是谓词
谓词是指普通函数或重载的operator()返回值是bool类型的函数对象(仿函数)。如果operator接受⼀个参数,那么叫做⼀元谓词,如果接受两个参数,那么叫做⼆元谓词,谓词可作为⼀个判断式
- 常⽤的遍历算法有哪些
//遍历容器元素
for_each(iterator beg, iterator end, _callback);
//将指定容器区间元素搬运到另⼀容器中
transform(iterator beg1, iterator end1, iterator beg2, _callbakc);
- 常⽤的查找算法有哪些
//查找元素
find(iterator beg, iterator end, value);
//条件查找
find_if(iterator beg, iterator end, _callback);
//查找相邻重复元素
adjacent_find(iterator beg, iterator end, _callback);
//⼆分查找法
bool binary_search(iterator beg, iterator end, value);
//统计元素出现的次数
count(iterator beg, iterator end, value);15、常⽤拷⻉和替换算法
- 常⽤的排序算法有哪些
//容器元素合并
merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
//容器元素排序
sort(iterator beg, iterator end, _callback);
//对指定范围内的元素随机调整次序
random_shuffle(iterator beg, iterator end);
//反转指定范围的元素
reverse(iterator beg, iterator end);
15、常⽤拷⻉和替换算法
//copy算法 将容器内指定范围的元素拷⻉到另⼀容器中
copy(iterator beg, iterator end, iterator dest)
//replace算法 将容器内指定范围的旧元素修改为新元素
replace(iterator beg, iterator end, oldvalue, newvalue)
//replace_if算法 将容器内指定范围满⾜条件的元素替换为新元素
replace_if(iterator beg, iterator end, _callback, newvalue)
//swap算法 互换两个容器的元素
swap(container c1, container c2)
16、常⽤算法⽣成算法
//accumulate算法 计算容器元素累计总和
accumulate(iterator beg, iterator end, value)
//fill算法 向容器中添加元素 fill(iterator beg, iterator end, value)
17、常⽤集合算法
//set_intersection算法 求两个set集合的交集
set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2,
iterator dest)
//set_union算法 求两个set集合的并集
set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator
dest);
//set_difference算法 求两个set集合的差集
set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2,
iterator dest)
基础及系统编程技术篇
1、shell是什么
Shell 是“命令解释器”(⼈和内核打交道的翻译)
Shell 作⽤:对⽤户输⼊到终端的命令进⾏解析,调⽤对应的执⾏程序
2、常⻅的 shell 及查看 shell 的⽅法
unix默认的shell为sh linux默认的shell为bash
查看shell的⽅法:cat /etc/shells 或 echo $SHELL
3、简单描述⼀下linux下的⽬录结构
/bin 存放系统可执⾏程序(⼤部分系统命令)
/sbin 存放 root ⽤户的系统可执⾏程序
/boot 存放内核 和启动程序的相关⽂件
/lib 库⽬录,存放系统最基本的动态库
/media 默认挂载设备媒体的⽬录,例如 U 盘、光驱
/mut 推荐挂载设备媒体的⽬录
/usr ⽤于存放庞⼤⽽复杂的⽬录(unix system resource,⽤于安装软件的⽬录)
/proc 系统内存的映射(随着系统的运⾏,时⻓变化的)
/ect 系统软件的启动和配置⽬录
/dev ⽤于存放设备⽂件 /home 家⽬录,所⽤⽤户的根⽬录(当前⽤户的根⽬录是/home/user)
4、linux下的权限分为哪3个组
⽤户权限、同组⽤户权限、其他⽤户权限
5、linux下⽂件的类型
b 块设备⽂件 c 字符设备⽂件 d ⽬录⽂件 -普通⽂件 l (软)连接⽂件
p 管道⽂件 s 本地套接字 // ⽹络编程中介绍
6、描述⼀下软连接和硬链接的区别
软链接:ln -s 源⽂件 ⽬标⽂件
硬链接:ln 源⽂件 ⽬标⽂件 源⽂件:即你要对谁建⽴链接,
- 软链接可以理解成快捷⽅式。它和windows下的快捷⽅式的作⽤是⼀样的。
- 硬链接等于cp -p 加同步更新。
7、谈谈你对⽂件描述表的理解
内核区-->进程管理 --> PCB 进程控制块(超⼤结构体)--> ⽂件描述符表(⽤于寻磁盘⽂件) ⼀个进程会对应⼀个⽂件描述符表,每打开⼀个⽂件会占⽤⼀个位置 ⼀个⽂件描述表本质上是⼀个数组,最多可 以容纳 1024(编号:0-1023)个⽂件描述符 前 3 个(0-2)默认是打开状态的(被占⽤),分别的标 准输⼊、标准输出、标准错误
8、谈谈你对程序和进程的理解
程序: 本质:⼆进制⽂件,可以运⾏,但还没有运⾏ 占⽤磁盘空间,不占⽤CPU 和内存(系统资源)
进程: 本质:正在执⾏的程序 占⽤CPU 和内存等更多的系统资源,⼀般不占⽤磁盘空间(I/O 操作可能会占⽤磁盘空间)
站在程序员的⻆度:进程是⼀系列指令的执⾏过程 站在操作系统的⻆度:进程是分配系统的资源的最⼩单位 关系: ⼀个程序可以对应多个进程,但⼀个进程只能对应⼀个程序
9、谈谈你对并⾏和并发的理解
并发:
1、CPU 将 1s 分成若⼲个时间碎⽚,每个时间碎⽚CPU 只能执⾏⼀个进程的⼀⼩部分
2、以时间碎⽚为单位,若⼲个进程循环占有CPU,并执⾏对应进程的⼀⼩部分
3、经过 n 次循环占有 CPU, 每个进程才能执⾏完毕 即:多个进程以时间碎⽚为单位,循环占有CPU,以完成同时执⾏的现象称为并发
注意:CPU 处理是纳秒级,⾁眼识别是毫秒级的,虽然每个进程的执⾏是间断的,但⾁眼感官上是连续的。
并⾏: 并发发⽣在单核(只能有⼀个并发链) 并⾏发⽣在多核(可以有多个并发链)多个程序在同一个时间段同时执行。
- 谈谈你对MMU(内存管理单元)的理解
将物理内存映射为虚拟内存
11、谈谈你对PCB(进程控制块)的理解
PCB 本质是⼀个超⼤的结构体 PCB 结构体中重要属性:
进程 pid :进程的唯⼀编号,类型 pid_t,⽆符号整形
进程状态:就绪、运⾏、挂起、停⽌ CPU 寄存器:进程只能短时间的占有 CPU,它⽤于进
程切换时候保存和恢复进程的执⾏进度
描述虚拟地址空间的信息:每启动⼀个进程,就会对应⼀个虚拟地址空间(理论值 0 - 4G) 描述控制终端的信息
当前⼯作⽬录 umask 掩码:默认 0002,每⼀进程都有⾃⼰的 umask 掩码 ⽂件描述符表 和信号相关的信息
⽤户 id 和组 id 进程组和会话:多个进程构成⼀个进程组,多个进程组构成⼀个会话 进程可以使⽤的资源上限:⽐如⽂件描述符的最⼤个数、管道 和 栈 的默认缓存区上限等
12、进程的状态有⼏种
进程的状态:等待态 执⾏态 就绪态
就绪态:⼀切条件都具备 只差cpu时间⽚到来
等待态:缺少执⾏条件
执⾏态:cpu时间⽚到来 执⾏
13、谈谈你对fork创建的进程的数据共享模式
fork() 后,其实是拷⻉⽗进程内容给⼦进程(只有进程pid 不同) 数据:刚创建⼦进程时,⽗⼦⽤户区数据完全相同,但⽗⼦后续各⾃进⾏不同操作,互不影响,各个进程的数据是完全独⽴的
原理剖析:进程运⾏时候,所有的计算都是在物理内存中的,⽗⼦进程“虚拟地址空间 ⽤户区中的数据”都会通过 MMU 映射到物理内存
问题 1:⽗⼦进程的数据都会映射到内存中,此时在内存中,会有⼏份数据?
答:⼀份(早期 Linux 系统设计是两份,但那种设计太浪费内存空间)
问题 2:那如何实现⽗⼦进程的数据独⽴的?
答:读时共享,写时复制(⽗⼦有⼀个进程对数据进⾏修改操作,都是先复制出⼀份,然后修改复制数据)
问题3:⽗⼦进程能否通过全局变量进⾏通信?
答:不能,因为是⽗⼦的全局变量是相互独⽴的进程访问的都是各⾃的经过复制的变量,内存不能共享,⽆法通信 PCB 中的⽂件描述符表、内存映射区:是共享的 例如: ⽗进程先打开⼀个⽂件,此时⽗进程⽂件描述符表第 3 号位被占⽤,指向⽂件, 此时fork,⼦进程会拷⻉⽗进程的⽂件描述符表,⼦进程第 3 号也指向该⽂件,意味着⽗⼦进程可以操 作同⼀个⽂件 这种设计的原因:是⽗⼦进程通过 pipe、fififo、内存映射区、套接字 等实现⽗⼦进程通信 的基础
14、请说出啥叫僵⼫进程以及孤⼉进程
孤⼉进程:不是没有爹!(会被领养) 过程:⽗进程先结束,⼦进程还运⾏,⼦进程称为孤⼉进程 注意:孤⼉会被 init 进程领养(init 进程是所有孤⼉进程的⽗进程)
设计领养⽬的:为了⼦进程结束后,可以回收⼦进程占有的系统资源 (进程结束后,⼦进程⾃⼰能够释放⽤户区空间,但⽆法释放 PCB,只能由⽗亲释放) 僵⼫进程:是⼀个死的进程 过程:⼦进程结束, ⽗进程没有回收⼦进程的 PCB,此时,⼦进程称为僵⼫进程
15、进程间的通信⽅式有⼏种?它们之间的特点?
⽆名管道、有名管道、信号、消息队列、共享内存、socket、
信号量:广泛用于进程或线程间的同步和互斥
消息队列:是消息的链表,存放在内存中,由内核维护
管道:最简单、数据只能读取⼀次半双⼯、匿名管道只能是有⾎缘的关系间通信:
命名管道:⽤于没有⾎缘关系之间的进程间通信
共享内存:效率⾼、不需要太多次的数据拷⻉,可以直接进⾏读写,缺点是不能保证数据同步,只能借助信号量保证同步
信号:简单、携带的信息量少,使⽤在特定的场景,优先级⾼。建议不要使⽤信号进⾏进程间通信,因为信号的优先级⾼会打破原有进程的执⾏过程
socket:主要⽤于⽹络中的进程间通信,通信过程以及数据复杂,但安全可靠。
16、谈谈守护进程的特点以及创建过程
特点:后台服务程序、不受终端控制、周期执⾏某个任务、不受⽤户登陆注销影响、⼀般以d结尾的名字 守护进程是个特殊的孤儿进程,这种进程脱离终端,
创建:
1创建子进程 2子进程设置会话 3更改目录 4设置掩码 5关闭文件描述符
1、创建⼦进程,⽗进程退出
2、⼦进程创建新会话(该⼦进程既是会⻓也是组⻓:⼀个进程构成⼀个进程组,构成⼀个会话)
3、改变当前⼯作⽬录 int chdir(const char *path) //避免因为原⼯作⽬录由于某种原因失效,影响进程
4、重设⽂件掩码 umask:由于第⼀步的 fork,⼦进程会继承⽗进程的掩码,默认 0002 umask(0),创建⽂件指定什么权限就是什么权限
5、关闭⽂件描述符:因为已经脱离终端,所以标准输⼊、输出、错误没有⽤了
6、执⾏核⼼⼯作
17、多进程和多线程的区别
进程是最⼩的系统资源分配单位,线程是最⼩CPU调度的执⾏单位 线程依赖于进程
进程始终共享的资源:代码段.txt,⽂件描述符,内存映射区
多线程始终共享的资源:堆、全局⽐变量 使⽤多线程好处:可以更简单的实现通信;⽽且可以节省资源 多线程共享虚拟地址空间); 更合理的利⽤CPU(如果有两个 CPU,对于⼀个进程,只能占⽤⼀个,但创建⼀个线程,两个线程就都可以跑,在内核看来,占⽤同样的地址空间,但有 2 个进程在运作)
18、能够⽤exit退出某个线程?
不能! exit 是⽤来退出进程的,因为⽗⼦线程共享同⼀地址空间,如果在某线程中使⽤,会使所有线程全退出 因此需要使⽤别的函数 pthread_exit (注意:⼦线程使⽤ return NULL 也可以,如上⾯例⼦) 单个线程退出函数:void pthread_exit(void *retval); -- 使某个线程退出,⽽不影响其他线程。
- 谈谈你对线程分离的理解
⼀般主控线程会回收⼦线程的资源,但是有时候需要⼦线程⾃⼰回收⾃⼰回收 PCB,因此调⽤该函数之后,不需要调⽤ pthread_join,设置了线程分离,该线程就是不可回收的,使⽤ pthread_join 反⽽会报错。
20、谈谈你对同步互斥的理解
互斥:是指某⼀资源同时只允许⼀个访问者对其进⾏访问,具有唯⼀性和排它性。但互斥⽆法限制访问者对资源的访问顺序,即访问是⽆序的
同步:是指在互斥的基础上(⼤多数情况),通过其它机制实现访问者对资源的有序访问。在⼤多数情况下,同步已经实现了互斥,特别是所有写⼊资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源.
总之:同步是⼀种更为复杂的互斥,⽽互斥是⼀种特殊的同步。
21、同步互斥的⽅式有哪些?
互斥锁、读写锁、条件变量、信号量
⽹络编程篇
1、及如何看待协议这个概念?
从应⽤的⻆度出发,协议可理解为“规则”,是数据传输和数据解释的规则 数据的发送⽅和接收⽅要严格遵照这些规则(这些规则肯定是之前就定好的)
例如:第⼀次发送⽂件名,第⼆次传输⽂件⼤⼩,第三 次发送⽂件内容 ---- FTP 协议的雏形 试想:如果双⽅不遵照这个规则,就会出现数据混乱
2、⽹络程序的设计模式有哪些?
C/S 模式: 传统的⽹络应⽤设计模式,客户机(client) / 服务器(server)模式。需要在通讯两端各⾃部署客户机和服务器来完成数据通信
优点:协议可以⾃定义(灵活) 数据可以提前缓存到本机上,后续运⾏快
缺点:客户端安装在主机电脑上,对⽤户的安全有⼀定威胁 需要分别开发客户端和服务器,⽽且需要联合调试,⼯作量⼤ 使⽤场景:数据量访问⽐较⼤,要求稳定性较⾼
B/S 模式: 浏览器(browser) / 服务器(server)模式。只需在⼀端部署服务器,⽽另外⼀端使⽤每台PC 都默认配置的浏览器即可完成数据的传输。
优点:相对安全、⼯作量⼩、跨平台
缺点:协议是定死的,不能提前数据缓存使⽤场景:数据访问量较⼩
- 谈谈⽹络的分层结构七层模型:
物理层:不是指具体的物理设备 指的是物理设备的标准制定 (⽹线 光纤的接⼝类型 ⽹卡的电流强弱) ⽐特流
数据链路层:负责完整的帧数据收发 (帧数据可以独⽴在⽹络传输的数据) mac地址封装和解封装交换器就⼯作在这⼀层
⽹络层:负责IP地址封装和解封装 逻辑主机的识别 路由器⼯作在这⼀层 数据包
传输层:负责的是协议端⼝的封装和解封装 识别不同的进程通信 数据段 分组和重组
会话层:通过传输层 建⽴通信线路 发送或接受请求
表示层:对数据进⾏加密解密 压缩解压缩 将计算机能识别的信息转换成⼈能识别的信息
应⽤层:具体的⽹络通信app( QQ FQ Weixin 浏览器 )
四层模型:
应⽤层:FTP ⽂件传送协议 telnet远程登录协议 http超⽂本传送协议
传输层:不同进程识别(端⼝) TCP 传输控制协议 UDP⽤户数据报协议
⽹络层:不同主机识别(IP) IP ⽹际协议 ICMP⽹络控制报⽂协议(ping命令)
数据链路层:不同物理设备(⽹卡)的识别(MAC地址) arp 地址解析协议(ip—>mac) rarp逆地址解析协议(mac———>ip)
4、我们是如何知道要前往的下⼀块⽹卡的mac地址(⽬ 的mac地址)?
通过arp表以及arp协议。链路层先查看路由表中是否记录了下⼀块⽹卡的mac地址,如果没有就调⽤arp协议获得下⼀块⽹卡的mac地址。
继续追问:能够描述⼀下arp协议的格式
5、如果判断⼀个⽹段的⼴播地址以及⽹段地址?
主机ID全为1就是⽹段的⼴播地址,主机ID全为0就是该⽹段的⽹段地址。
6、分别描述⼀下IP地址以及端⼝的作⽤
IP地址在⽹络环境中表示唯⼀的主机,端⼝可以在主机中表示唯⼀的进程。
⽹络层定义了源和⽬的 IP,传输层定义了源和⽬的的端⼝,这样就可以指定由“源主机的某进程”到“⽬的主机的某进程”的通信
7、谈谈你对字节序的理解
字节序就是主机存放数据的顺序,⼀般在异构计算机以及多字节的时候才讨论字节序。字节序的分为本地字节序,⽹络字节序。字节序的存储⽅式有⼤端存储以及⼩端存储,⽽⽹络字节序就是⼤端存储。
- 描述⼀下三次握⼿以及四次挥⼿的过程
9、描述⼀下TCP与UDP的区别
TCP与UDP都是传输层协议,进程与进程间的通信。
TCP传输控制协议:⾯向连接编程,需要建⽴连接—>使⽤链接—>关闭链接,失败重传、排序检错,⾯ 向数据传输,不⽀持⼴播、多播。
UDP⽤户报⽂协议:⾯向⽆连接编程,不排序,不重重传,不检错,简单的应答服务,⽀持⼴播、多播。
10、谈谈select的优缺点
优点:⼀个线程就可以⽀持多个客户端(多路 IO 都有这个优点!),可以跨平台(select 独有) 缺点:同时监听⽂件描述符的上限是 1024 个 注意:不是因为打开⽂件的上限是 1024(上限数可以修改),⽽是因为在 select 底层实现时候,fd_set使⽤了宏 FD_SETSIZE = 1024返回值是⼀个数量,需要循环遍历判断到底谁符合条件,因此⾼并发少访问的时候,效率低 监听 集合和满⾜监听条件的集合是同⼀个集合,select 后会改变原监听集合,使其⽆法再次使⽤,因此,select 前需要将原监听集合保存
11、谈谈poll相较于select的优点
select 的监听和返回的集合是不分离的,是通过传⼊传出的⽅式实现⽽ poll 通过“参数 1”的结构体实现请求和返回分离 poll 相较于 select 的劣势:不能跨平台,只能在linux 下使⽤,导致 poll 地位⽐较尴尬
12、谈谈不同局域⽹的通信
数据从应⽤层到达传输层进⾏源/⽬的端⼝的封装,将数据传输到⽹络层。⽹络层封装IP数据报⽂,并判断⽬的IP是否是当前局域⽹,将封装好的IP数据包发送链路层,链路层先到arp表找下⼀块⽹卡的mac地址,如果存在,将封装源mac和⽬的mac,如果arp表中没有对应的mac就要调⽤arp协议⼴播得到下⼀块⽹卡的mac地址,然后在封装mac报⽂。⽹络数据到达路由器,路由器先查看数据包的⽬的ip是否与路由器的某个本地⽹卡是同⼀个⽹段,如果是就修改数据包的源mac和⽬的mac,发送出去,如果不是同⼀个⽹段,路由器将查看路由表,寻找下⼀跳,根据下⼀跳寻找当前路由器数据出去的设备,让后将修改源mac为出去设备的mac,⽬的mac为下⼀跳的mac,将数据发送到下⼀跳,这样重复这个动作将数据发送到⽬的主机。
13、谈谈集线器、交换机、路由器的作⽤
集线器:Hub 发送到集线器上的数据 会被集线器⼴播到连接改集线器的所有主机上共享带宽。 整形放⼤⼯作在物理层
交换机:switchs 单播 独享带宽 有⾃主学习功能(mac)拓展⽹络接⼝⼯作在链路层(⼆层交换机)3层交换机(核⼼层在第⼆层 具备⽹段划分的功能 VLAN
Qt篇
1、谈谈信号和槽机制
信号槽是 Qt 框架引以为豪的机制之⼀。所谓信号槽,实际就是观察者模式。当某个事件发⽣之后,⽐ 如,按钮检测到⾃⼰被点击了⼀下,它就会发出⼀个信号(signal)。这种发出是没有⽬的的,类似⼴播。如果有对象对这个信号感兴趣,它就会使⽤连接(connect)函数,意思是,将想要处理的信号和⾃⼰的⼀个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会⾃动被回调。这就类似观察者模式:当发⽣了感兴趣的事件,某⼀个操作就会被⾃动触发