「目录」
本篇参考网上及自身的面试经验,总结一些高频考察的Linux C/C++知识点,方便后续查阅总结。
static_cast<type>(expression)
该运算符把 expression 转换为 type 类型,主要用于基本数据类型之间的转换,如把 uint 转换为 int,把 int 转换为 double 等。
另外,static_cast 还可用于类层次结构中,基类和派生类之间指针或引用的转换,但也要注意:
static_cast 进行上行转换是安全的,即把派生类的指针转换为基类的;
static_cast 进行下行转换是不安全的,即把基类的指针转换为派生类的。
注:static_cast 没有运行时类型检查来保证转换的安全性,需要程序员来判断转换是否安全。uint x = 1;
int y = static_cast<int>(x); // 转换正确
int x = 1;
double y = static_cast<double>(x); // 转换正确
// 上行转换,派生类→基类
Derive* d = new Derive();
Base* b = static_cast<Base*>(d);
// 下行转换,基类→派生类
Base* b = new Base();
Derive* d = static_cast<Derive*>(b);
const_cast<type>(expression)
主要是用来去除复合类型中const和volatile属性(没有真正去除)。expression 和 type 的类型需要保持一致。dynamic_cast<type>(expression)
主要用于类层次间的上行转换或下行转换。在进行上行转换时,dynamic_cast 和 static_cast 的效果是一样的,但在下行转换时,dynamic_cast 具有类型检查的功能,比 static_cast 更安全。reinterpret_cast<type>(expression)
该运算符可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针。除此之外,在C++中,static也可以类成员变量和类成员函数。
共同点:
不同点:
容器分为两大类: 顺序容器和关联容器
__cplusplus
就会被定义 如果是一个c文件在被编译那么__STDC__
就会被定义。// 将第index为置1. index: 0~7
unsigned char set_bit(unsigned char value, int index)
{
return value | (1 << index);
}
//将第index位清0, index: 0~7
unsigned char clear_bit(unsigned char value, int index)
{
return value & (~(1 << index));
}
注:linux下C和C++默认库环境路径:/usr/include
把一个方法与其所在的类/对象关联起来叫做方法的绑定。[22]
「静态类型」:对象在声明时采用的类型,在编译期既已确定。
「动态类型」:通常是指一个指针或引用目前所指对象的类型,是在运行期决定的。
「静态绑定」:绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期。
「动态绑定」:绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期。
非虚函数一般都是静态绑定,而虚函数都是动态绑定(如此才可实现多态性)
extern "C"
的 主要 作用 就 是 为了能够正确实现C++代码调用其他C语言代码。加上extern "C"
后,编译器会按照C语言语法进行编译。typedef和define都是替一个对象取一个别名,以此增强程序的可读性。[23] ①作用阶段不同 「typedef」: 编译阶段有效, 有类型检查的功能。 「define」: 预处理阶段有效, 编译前, 只进行简单而机械的字符串替换, 不进行任何检查。 ② 功能不同 「typedef」: 用来定义类型(内部或自定义类型)的别名, 起到使类型易于记忆的功能。 「define」: 不止可以为类型取别名, 还可以定义常量, 变量, 编译开关等。 ③ 作用域不同[24] 「typedef」: 与变量生命期类似。放在函数外,作用域从定义开始到文件尾;若放在函数内,定义域从定义处到该函数结尾。 「define」 没有作用域的限制,从定义开始到文件结尾。
//源自https://www.cnblogs.com/retry/p/14045305.html
//a.c
typedef …//此处开始到文件结尾
#define …//此处开始到文件结尾
int negate(int num)
{
…
typedef …//此处开始到该函数结束。注意,该函数内,此定义前,也不能用
#define …//此处开始到文件结尾
…
}
typedef …//此处开始到文件结尾
#define …//此处开始到文件结尾
void show()
{
typedef …//此处开始到该函数结束。
#define …//此处开始到文件结尾
}
④ 对指针的操作: 二者修饰指针类型时, 作用不同。
Typedef int * pint;
#define PINT int *
const pint p;//p不可更改,p指向的内容可以更改,相当于 int * const p;
const PINT p;//p可以更改,p指向的内容不能更改,相当于 const int *p;或 int const *p;
pint s1, s2; //s1和s2都是int型指针
PINT s3, s4; //相当于int * s3,s4;只有一个是指针。
friend
修饰另一个已经定义的类B,类B就属于类A的友元类,类B中的所有成员函数都是类A的友元函数,类B可以访问类A的所有成员,包括 public
、protected
、private
属性的。select、poll、epoll是多路复用主要用到的三种技术,主要区别如下:
① 支持一个进程所能打开的最大连接数 「select」 单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是3232,同理64位机器上FD_SETSIZE为3264),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试。 「poll」 poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的。 「epoll」 虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接。
② FD剧增后带来的IO效率问题 「select」 因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。 「poll」 同上。 「epoll」 因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。
③ 消息传递方式 「select」 内核需要将消息传递到用户空间,都需要内核拷贝动作。 「poll」 同上。 「epoll」 epoll通过内核和用户空间共享一块内存来实现的。
① 第一次握手 客户端给服务器发送一个SYN段(在 TCP 标头中 SYN 位字段为 1 的 TCP/IP 数据包), 该段中也包含客户端的初始序列号(Sequence number = J)。
② 第二次握手 服务器返回客户端 SYN +ACK 段(在 TCP 标头中SYN和ACK位字段都为 1 的 TCP/IP 数据包), 该段中包含服务器的初始序列号(Sequence number = K);同时使 Acknowledgment number = J + 1来表示确认已收到客户端的 SYN段(Sequence number = J)。
③ 第三次握手 客户端给服务器响应一个ACK段(在 TCP 标头中 ACK 位字段为 1 的 TCP/IP 数据包), 该段中使 Acknowledgment number = K + 1来表示确认已收到服务器的 SYN段(Sequence number = K)。
为了实现可靠数据传输,TCP 协议的通信双方,都必须维护一个序列号,以标识发送出去的数据包中,哪些是已经被对方收到的。三次握手的过程即是通信双方相互告知序列号起始值,并确认对方已经收到了序列号起始值的必经步骤。
① 第一次挥手:Client端发起挥手请求,向Server端发送标志位是FIN报文段,设置序列号seq,此时,Client端进入FIN_WAIT_1状态,这表示Client端没有数据要发送给Server端了。
② 第二次挥手:Server端收到了Client端发送的FIN报文段,向Client端返回一个标志位是ACK的报文段,ack设为seq加1,Client端进入FIN_WAIT_2状态,Server端告诉Client端,我确认并同意你的关闭请求。
③ 第三次挥手:Server端向Client端发送标志位是FIN的报文段,请求关闭连接,同时Client端进入LAST_ACK状态。
④ 第四次挥手 :Client端收到Server端发送的FIN报文段,向Server端发送标志位是ACK的报文段,然后Client端进入TIME_WAIT状态。Server端收到Client端的ACK报文段后,关闭连接。此时,Client端等待2MSL的时间后依然没有收到回复,则证明Server端已正常关闭,Client端也关闭连接。
其实是客户端和服务端的两次挥手,也就是客户端和服务端分别释放连接的过程。客户端在发送完最后一次确认之后,还要等待2MSL的时间。有两个原因: 一是为了让B能够按照正常步骤进入CLOSED状态,二是为了防止已经失效的请求连接报文出现在下次连接中。
① 由于客户端最后一个ACK可能会丢失,这样B就无法正常进入CLOSED状态。于是B会重传请求释放的报文,而此时A如果已经关闭了,那就收不到B的重传请求,就会导致B不能正常释放。而如果A还在等待时间内,就会收到B的重传,然后进行应答,这样B就可以进入CLOSED状态了。
② 在这2MSL等待时间里面,本次连接的所有的报文都已经从网络中消失,从而不会出现在下次连接中。
sizeof()
判断系统是16位还是32位?
可通过内存地址地址长度判断,定义一个指针变量,通过打印其地址即可判断。从操作系统角度来看,进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)。
这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。
简单的分为连续分配管理方式和非连续分配管理方式这两种。连续分配管理方式是指为一个用户程序分配一个连续的内存空间,比如块式管理。非连续分配管理方式允许一个程序内存分散,比如页式管理、段式管理和段页式管理。
int JudgeSystem(void) {
int a = 1;
char *p = (char *)&a;
// 如果是小端则返回 1,如果是大端则返回 0
return *p;
}
int JudgeSystem(void) {
union {
char c;
int i;
} un; // 匿名联合体 un
un.i = 1;
// 如果是小端则返回 1,如果是大端则返回 0
return un.c;
}
通过快慢指针。慢指针每次走1步,快指针每次走2步。当快慢指针相遇,说明链表为循环链表。
bool IsRingList(SNode* pHead)
{
bool ret = false;
SNode *pSlow = pHead, *pFast = pHead;
while(pFast && pFast->next)
{
pSlow = pSlow->next;
pFast = pFast->next->next;
if (pSlow == pFast) {
return true;
}
}
return ret;
}
通过快慢指针。慢指针每次走1步,快指针每次走2步,两者速度差为1步。当两指针相遇时,快指针比慢指针多走1圈。
int GetRingListLength(SNode* pHead)
{
int length = 0;
SNode *pSlow = pHead, *pFast = pHead;
while(pFast && pFast->next)
{
pSlow = pSlow->next;
pFast = pFast->next->next;
length++;
if (pSlow == pFast) {
break;
}
}
if (pSlow != pFast)
{
length = 0;
printf("It is not ring list.\n");
}
return length;
}
通过快慢指针,快指针先走n步,随后慢指针与快指针同步走,当快指针到达链表尾部时,慢指针即为倒数第n个节点。
[1]
C++四种强制类型转换]: https://zhuanlan.zhihu.com/p/258975506
[2]
static的作用: https://www.cnblogs.com/lit10050528/p/3910271.html
[3]
C++ const关键字小结: https://www.runoob.com/w3cnote/cpp-const-keyword.html
[4]
#define和const的区别: https://blog.csdn.net/yi_ming_he/article/details/70405364
[5]
C++中指针与引用的区别: https://zhuanlan.zhihu.com/p/140966943
[6]
C++中指针常量和常量指针的区别: https://www.cnblogs.com/lizhenghn/p/3630405.html
[7]
将“引用”作为函数返回值类型的格式、好处和需要遵守的规则: https://blog.csdn.net/weibo1230123/article/details/82014538
[8]
C++内存管理全景指南|C++硬核指南: https://zhuanlan.zhihu.com/p/368901281
[9]
C++——malloc/free和new/delete的区别: https://blog.csdn.net/WEIYANGBIN/article/details/109699674
[10]
为什么构造函数不能声明为虚函数,析构函数可以: https://www.cnblogs.com/lpxblog/p/5890933.html
[11]
C++打怪 之 vector: https://mp.weixin.qq.com/s?__biz=MzUzNjExNjMyNA==&mid=2247484576&idx=1&sn=c55235b7632bb18c05bcebb02436d07e&chksm=fafa69abcd8de0bd514592592c65730590ab00ad8f48b0de3dc0b7ce809d4ee5a45e1509e1dc&token=1237879194&lang=zh_CN#rd
[12]
从零开始,打造自己的STL(五、deque): https://zhuanlan.zhihu.com/p/35087078
[13]
std::set API参考文档: https://www.apiref.com/cpp-zh/cpp/container/set.html
[14]
C++ multiset,STL multiset详解: http://c.biancheng.net/view/386.html
[15]
std::map API参考文档: https://www.apiref.com/cpp-zh/cpp/container/map.html
[16]
C++ STL multimap容器用法完全攻略(超详细): http://c.biancheng.net/view/7190.html
[17]
C++中内存泄漏的几种情况: https://www.cnblogs.com/liushui-sky/p/7727865.html
[18]
c++11新特性,所有知识点都在这了: https://zhuanlan.zhihu.com/p/139515439
[19]
C++构造函数和析构函数的调用顺序: https://blog.csdn.net/xyzyhs/article/details/82356948
[20]
构造函数为什么一般不定义为虚函数?而析构函数一般写成虚函数的原因: https://blog.csdn.net/libaineu2004/article/details/85684224
[21]
栈溢出几种情况及解决方案: https://blog.csdn.net/Beyond_2016/article/details/81286223
[22]
C++中的静态绑定和动态绑定: https://www.cnblogs.com/lizhenghn/p/3657717.html
[23]
define 和 typedef 区别: https://www.jianshu.com/p/53ac91a23979
[24]
#define与typedef作用域: https://www.cnblogs.com/retry/p/14045305.html
[25]
C++友元函数和友元类(C++ friend关键字): http://c.biancheng.net/view/2233.html
[26]
C++程序性能优化指南: https://segmentfault.com/a/1190000039136866
[27]
select、poll、epoll之间的区别(搜狗面试): https://www.cnblogs.com/aspirant/p/9166944.html
[28]
TCP和UDP的区别: https://blog.csdn.net/weixin_43796685/article/details/104558965
[29]
TCP三次握手详解-深入浅出(有图实例演示): https://blog.csdn.net/jun2016425/article/details/81506353
[30]
TCP 为什么三次握手而不是两次握手(正解版): https://blog.csdn.net/lengxiao1993/article/details/82771768
[31]
一文彻底搞懂 TCP三次握手、四次挥手过程及原理: https://www.cnblogs.com/cooffeeli/p/TCP_Establish_Connection_Close_Connection.html
[32]
TCP四次挥手及原因: https://www.cnblogs.com/GuoXinxin/p/11657933.html
[33]
死锁的定义、产生原因、必要条件和处理方法: https://blog.csdn.net/zymx14/article/details/78046830
[34]
操作系统--brk()和mmap()详解: https://blog.csdn.net/shuzishij/article/details/86574927
[35]
操作系统常见的几种内存管理机制: https://blog.csdn.net/qq_38197844/article/details/108691639
[36]
常见的7种排序算法: https://blog.csdn.net/liang_gu/article/details/80627548
[37]
链表常见操作和15道常见面试题: https://zhuanlan.zhihu.com/p/265159396
用心感悟,认真记录,写好每一篇文章,分享每一框干货。
更多文章内容包括但不限于C/C++、Linux、开发常用神器等,可进入“开源519公众号”聊天界面输入“文章目录” 或者 菜单栏选择“文章目录”查看。公众号后台聊天框输入本文标题,在线查看源码。