面试题(三)
20、CA证书
服务器安装数字证书,客户端安装根证书
CA的颁发机构、有效期、签名、公钥、证书所有者
21、公钥和私钥有什么区别
22、三次握手 四次挥手 为什么要四次
服务端数据没有发送完,二三次不能合并
23、四次挥手完会立马关闭吗(会监听一段时间 这个时间具体是多少?)
2MSL,保证服务端接收到最后一个ACK正常关闭
24、tcp如何保持可靠传输
序列号、确认号、超时重传
窗口
拥塞控制:慢启动、窗口阈值、快恢复、快速重传
25、拥塞控制 流量控制
26、具体说一下拥塞窗口cwnd
27、为什么要分段和分页,分段和分页的优缺点
**分段(Segmentation)和分页(Paging)**是现代操作系统管理内存的两种主要方法,它们都有各自的优缺点,通常结合使用以获得更好的性能和灵活性。
1. 分段(Segmentation)
分段是内存管理的一种方式,将逻辑内存划分为不同的段(segment),每个段代表一个逻辑单位,如代码段、数据段或堆栈段。每个段有独立的基址和大小。
优点:
- 逻辑结构清晰:程序按照逻辑模块划分段,符合程序的自然结构,使得管理和访问更加直观。
- 方便共享和保护:可以对不同段设置不同的访问权限,从而实现进程间的数据共享和内存保护。
- 动态增长:段可以动态增长,特别适合某些需求不断增加的结构(如堆栈)。
缺点:
- 外部碎片:由于段的大小是可变的,随着段的分配和释放,内存中可能出现外部碎片,使得内存利用效率降低。
- 管理复杂:段表较复杂,尤其是当段数较多时,管理段的基址和大小需要更多的资源。
2. 分页(Paging)
分页是另一种内存管理方式,它将物理内存和虚拟内存划分为固定大小的页(page)。虚拟地址空间被分成若干个等大小的页,而物理内存被分成等大小的页框(frame),虚拟页通过页表映射到物理页框。
优点:
- 无外部碎片:因为分页固定大小,内存分配和回收时不会产生外部碎片(即未被使用的大块内存)。
- 灵活的内存管理:可以将程序的页分散放置在物理内存的不同位置,灵活管理内存。
- 简单的内存保护:通过页表,每个页可以单独控制访问权限,易于实现内存保护和隔离。
缺点:
- 内部碎片:由于页的固定大小,可能会有部分内存没有被完全使用,产生内部碎片。
- 页表开销:需要维护页表,当虚拟地址空间较大时,页表可能会占用较多内存,且频繁的页表查找会影响性能。
3. 分段和分页的结合
在许多现代操作系统中,分段和分页结合使用,发挥两者的优势。例如,内存可以先分为不同的段,每个段再划分为若干固定大小的页,这种方式既能避免分页产生的内存碎片,又可以利用分段的逻辑结构。
结合的优点:
- 减少碎片:结合分段和分页,可以避免外部碎片问题,同时也能更好地组织和保护内存。
- 灵活的内存管理:结合两者的优点,既可以灵活调整段的大小,又可以使用分页进行细粒度的内存管理。
结合的缺点:
- 管理复杂度高:需要维护段表和页表两种数据结构,增加了内存管理的复杂性和开销。
总结
分段和分页各有优缺点,分段适合逻辑模块化的程序,分页则更擅长减少内存碎片并提供更灵活的内存管理。结合两者的方式能够更好地平衡这两者的缺点,实现高效的内存管理。
28、 vector list 区别
29、C++ 宏和内联函数区别,何时触发“优化”
在 C++ 中,宏和内联函数都可以在特定条件下触发优化,但它们的机制和触发优化的时机不同。
1. 宏 (Macros)
宏是在预处理阶段通过 #define
定义的文本替换。它们直接在编译前将代码片段替换为定义的内容,因此避免了函数调用的开销。
宏触发优化的时机:
- 消除函数调用开销:由于宏是直接展开的,代码中不会涉及到函数调用,因此减少了调用的额外开销。
- 纯文本替换:宏只是在代码中进行简单的替换,因此在频繁使用的地方可以通过减少函数调用来优化性能。
但由于宏是文本替换,它没有类型检查和作用域管理,可能引入难以发现的错误。
2. 内联函数 (Inline Functions)
内联函数通过 inline
关键字定义。它们与宏不同,会进行类型检查、作用域控制,并通过编译器决定是否进行内联展开。
内联函数触发优化的时机:
- 消除函数调用:当编译器决定将内联函数展开时,函数调用将被替换为函数体代码,从而避免函数调用开销。这对于小型、频繁调用的函数尤为有效。
- 编译器优化决策:尽管使用了
inline
关键字,但是否内联最终由编译器决定。编译器会根据函数的复杂性、大小、以及是否递归等因素判断是否展开函数。 - 类型检查和安全:与宏不同,内联函数具有类型安全性,编译器会确保函数使用的正确性,从而减少由于直接替换带来的潜在问题。
3. 优化触发的条件
无论是宏还是内联函数,优化触发的关键在于以下几个条件:
- 函数的大小:小的函数更有可能被内联,因为其展开后带来的性能提升大于展开的成本。
- 调用频率:在循环中或性能关键路径上频繁调用的函数,更有可能被编译器内联。
- 编译器优化等级:使用高等级优化选项(如
-O2
或-O3
)时,编译器会更积极地尝试内联小型函数。 - 代码复杂度:如果函数体过大或包含复杂的控制结构,编译器可能不会进行内联展开。
4. 宏与内联函数的区别
- 安全性:内联函数比宏更安全,具有类型检查和作用域管理,避免了宏带来的潜在错误。
- 编译器决策:宏总是直接替换,而内联函数是否展开则取决于编译器的判断。
- 调试和维护:内联函数在调试时更易追踪,而宏的错误可能更难发现,因为它们是在预处理阶段完成替换的。
示例
宏示例:
#define SQUARE(x) (x * x)
使用 SQUARE(5 + 1)
会展开为 (5 + 1 * 5 + 1)
,得到错误的结果。
内联函数示例:
inline int square(int x) {
return x * x;
}
调用 square(5 + 1)
会返回正确的 36
,并且由于是内联函数,可能避免函数调用的开销。
结论
- 宏 可以通过消除函数调用直接展开代码,提升性能,但缺乏类型检查和作用域控制,可能带来风险。
- 内联函数 提供更安全的替代方案,编译器在合适的情况下会进行内联优化,减少函数调用开销,提升性能。
通常,现代 C++ 更倾向于使用内联函数,因为它们在安全性、可维护性和调试方面相比宏有显著优势。
30、C++ 虚函数表的结构,是连续的吗,虚函数地址存放顺序
在 C++ 中,虚函数表(vtable)通常存放在静态内存区域或全局数据区,由编译器在程序的初始化阶段生成并管理。每个类只会有一个虚表,而每个包含虚函数的对象会包含一个指向该虚表的指针(vptr),通过它实现对虚函数的动态调用。
虚表存放的细节
-
静态区域:虚表是类级别的,而非对象级别,因此它是静态的,所有实例共享一个虚表。编译器会在类加载时将虚表分配在静态内存区域中,与全局或静态变量类似。因为虚表是不可变的(除非涉及动态装载的情况),所以存放在一个固定的、共享的区域能够有效节省空间。
-
vptr的位置:每个包含虚函数的对象会在对象内部存储一个指向虚表的指针(vptr),这个指针通常位于对象的最前面。通过这个指针,程序能够在运行时找到虚表,从而通过虚表中的函数指针进行动态绑定,调用正确的虚函数。
运行时机制
- 在程序运行时,当调用一个虚函数时,程序首先通过 vptr 找到对象所属类的虚表,并在虚表中找到对应函数的地址。这允许 C++ 实现运行时的多态性。
总结
虚函数表存放在静态的内存区域,由编译器在类定义时生成并初始化,而 vptr 则存储在每个对象中,指向该类的虚表,保证虚函数能够在运行时进行正确的动态调用。
31、 数据表「学号、姓名、分数、课程名、学分」如何优化
优化数据表「学号、姓名、分数、课程名、学分」的设计,可以从以下几个方面考虑:
1. 表的结构优化
- 分表设计:将信息拆分成多个具有更高内聚性的表,以避免数据冗余并提升查询效率。
- 学生表(Student):
- 学号(Primary Key)
- 姓名
- 课程表(Course):
- 课程ID(Primary Key)
- 课程名
- 学分
- 成绩表(Score):
- 学号(Foreign Key)
- 课程ID(Foreign Key)
- 分数
- 学生表(Student):
2. 添加索引
为提高查询效率,在常用的查询字段上创建索引:
- 学号索引:针对按学号查询的操作,建立索引可以快速定位学生。
- 课程ID索引:针对按课程名或课程ID查询的操作,使用索引可以加速检索。
3. 考虑数据库规范化
- 第一范式 (1NF):保证每个字段的值是不可再分的原子值,已满足。
- 第二范式 (2NF):消除非主属性对部分主属性的依赖,分离成绩表。
- 第三范式 (3NF):消除非主属性对非主属性的传递依赖,确保数据的独立性,姓名不依赖于成绩。
4. 冗余设计与缓存优化
如果需要提升查询性能,可以考虑添加适当的冗余字段或数据缓存:
- 总学分冗余字段:如果频繁查询某学生的总学分,可以将总学分预先计算并缓存。
- 缓存机制:使用数据库层面的缓存或分布式缓存系统(如 Redis)缓存学生成绩等常用数据,减少频繁的数据库查询压力。
5. 批量操作优化
对于频繁的批量插入、更新操作,可以使用事务处理或批量处理功能,减少操作的频次和锁的争用。
通过这些优化措施,可以提升表的性能和数据的一致性,并优化查询、插入等操作的效率。
原文地址:https://blog.csdn.net/SSuperwoman/article/details/142446058
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!