C++面经汇总(二)

ipv4和ipv6的区别?
1.在表现形式上
ipv4是32位,共有2的32次方个数字,ipv6则有128位。
ipv4基本表示时每个位都是10进制,ipv6每个位都是16进制。
IPv4协议的地址可以通过手动或DHCP配置的。
3.数据包的字节数不同。iPV6地址分配遵循Aggregation原则,路由器的路由表长度减少,提高转发数据包的速度。
4.在IPv6协议,地址解析协议(ARP)被邻居发现协议(NDP)的功能所取代。
5.IPv6提供身份验证和加密,但IPv4不提供。

参考链接:ipv4和ipv6的区别?

堆和栈的区别?(频率超高)

(1)管理方式不同。栈由操作系统自动分配释放;堆的申请和释放工作由程序员控制;(操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆节点,然后将该节点从空闲节点链表中删除,并将该节点的空间分配给程序。)
(2)生长方向不同。堆的生长方向向上,内存地址由低到高;栈的生长方向向下,内存地址由高到低。
(3)存放内容不同。栈存放的内容,函数返回地址、相关参数、局部变量和寄存器内容等。当主函数调用另外一个函数的时候,要对当前函数执行断点进行保存.堆,一般情况堆顶使用一个字节的空间来存放堆的大小,而堆中具体存放内容是由程序员来填充的。

(4)空间大小不同。每个进程拥有的**栈的大小要远远小于堆的大小。**理论上,程序员可申请的堆大小为虚拟内存的大小,进程栈的大小 64bits 的 Windows 默认 1MB,64bits 的 Linux 默认 10MB;

(5)分配效率不同。栈由操作系统自动分配,会在硬件层级对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是由C/C++提供的库函数或运算符来完成申请与管理,实现机制较为复杂,频繁的内存申请容易产生内存碎片。显然,堆的效率比栈要低得多。

(5)分配方式不同。堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是由操作系统完成的,比如局部变量的分配。动态分配由alloca函数进行分配(alloca() 不具可移植性, 而且在没有传统堆栈的机器上很难实现。 所以没有普及),但是栈的动态分配和堆是不同的,他的动态分配是由操作系统进行释放,无需我们手工实现。

堆栈空间大小是由什么决定的?

栈空间大小:
是根据参数和局部变量估计一个大小足够容纳活动记录的空间,因此为不同函数分配的栈空间也是不同的编译器默认栈大小设置的是所有函数占用栈空间的上限,不管是一个函数还是多个函数超过这个总数就会报告栈溢出。没有哪个傻子写的编译器傻到为每个函数分配固定的栈空间去浪费内存,那样的话写一个简单的递归就会溢出,同样在函数中定义一个大一点的数组也会溢出。

堆空间大小
手动申请释放,堆内存的大小受限于计算机系统中有效的虚拟内存。

为什么栈空间相比于堆很小?
1.C的栈在x86-64上是直接绑定到CPU指令的,实现上极其精简,因此它与堆不同,没有动态增长、动态缩小的功能,**一旦分配出来就会永远占用相应的空间。**每个线程都会占用独立的栈空间,这样对于线程数很多的进程来说,如果栈空间分配得过多,就会很浪费内存空间。相反,**堆空间可以一开始分配得很小,然后不停向上增长,释放相应的空间之后还可以归还给操作系统,因此适合处理比较大的空间。**对于一定要占用比较大的栈空间的情况,可以指定栈空间大小。
2.堆通常是进程内共用的,栈通常是线程独占的,一个进程包含多个线程,所以堆就有必要比栈大了。

Linux默认的用户栈空间的大小是8MB(软限制)

为什么经常听到栈溢出,却很少听到堆溢出?

堆溢出:不断的new 一个对象,一直创建新的对象。(堆溢出不了,当你申请访问超过的大小的时候会返回失败。)
栈溢出:死循环或者是递归太深,递归的原因,可能太大,也可能没有终止。
栈溢出实例:
int f(int x)
{
int a[10];
a[11] = x;
}

因为栈空间分配是系统自动分配的,例如当发生递归的时候,栈中存放的数据放过了系统分配的栈空间大小,就会发生溢出。而首先堆的空间远大于栈,其次堆一般是我们手动申请释放,一旦申请超出范围就会报错。

进程线程和协程的区别?(被问频率超高)

1.进程是资源分配的最小单位。 线程是CPU调度的基本单位。
2.进程拥有独立的内存单元。同一进程下的多个线程共享内存。线程只独享指令流执行的必要资源,如寄存器和栈。
3.进程间不会相互影响,多考虑通信。线程间会相互影响,多考虑同步,通信可不通过内核直接通信。
4.进程编程调试简单可靠性高,但是创建销毁切换开销大。线程正相反,开销小,切换速度快,但是编程调试相对复杂。

用户态和内核态有哪些交互方式?如何共享内存的?

内核到用户:printk函数,文件
用户态到内核态: 在linux中,用户对设备的操作往往被抽象为对文件的操作。利用这一特性,可以通过注册和实现伪字符设备到内核,来实现用户进程和内核空间的交互。
内核态和用户态相互通信:
(1)proc文件系统,是当前内核或内核模块,和用户交互的主要方式。
(2)netlink是一种特殊的socket,用于用户态与内核态的双向通讯。

参考链接:https://blog.csdn.net/LinSeeker85/article/details/86599507

tcp如果设置非保活机制(关闭keepAlive),如果客户端和服务器端一直没有数据发送,该链接还存在么?
还存在。
要区别TCP的keepAlive和http的keep-Alive.
参考链接:tcp链接

暂时未解决的
1.乐观锁和悲观锁?
这个是数据库的知识,暂时看不懂
https://www.cnblogs.com/qlqwjy/p/7798266.html

聪聪面经
1、多态机制
多态就是说同一个名字的函数可以有多种不同的功能。分为编译时的多态和运行时的多态。编译时的多态就是函数重载,包括运算符重载,编译时根据实参确定调用哪个函数。运行时的多态则和虚函数、继承有关。

2、那多态底层实现是怎么样的
利用虚函数表,先构建一个基类,然后在基类的构造函数中会建立虚函数表,也就是一个储存虚函数地址的数组,内存地址的前四个字节保存指向虚函数表的指针,然后当多个子类继承父类之后,主函数中可以通过父类指针调用子类的继承函数。
那子类的多态函数是怎么被调用的?
因为每个子类都继承并设置了自己的虚函数表,每次用用父类指针创建新子类时就会出现,从而最终调用自己的表。
3、构造函数可以是虚函数吗?为什么?
不可以,因为虚函数存在的主要目的就是为了多态。而子类并不继承父类的构造函数,构造函数是创建对象时自己主动调用的,不可能被继承,所以没有使父类构造函数变成虚函数的必要。另外,父类在构造函数中创建虚函数表,实例化类对象,如果构造函数成为虚函数,那么因为类对象没有实例化导致不可能后续出现虚函数。
4、析构函数呢?!!!!
当析构函数是非虚函数时,主函数通过指针访问非虚函数时,编译器会根据指针的类型来确定要调用的函数;而指针是父类指针,所以调用父类的析构函数。
析构函数必须是虚函数。因为如果不是虚函数,当在主函数中用父类的指针new出一个子类对象,最后析构的时候,只会调用父类析构函数而不会调用子类析构函数。而且如果不为虚函数,父类指针就不会调用子类成员函数。
父类析构函数成为虚函数时,子类的析构函数会自动也变为虚函数。这个时候编译器会忽略指针的类型,而根据指针的指向来选择函数;也就是说,指针指向哪个类的对象就调用哪个类的函数。pb、pd 都指向了派生类的对象,所以会调用派生类的析构函数,继而再调用基类的析构函数。

5、如果析构函数不是虚函数,一定会出现内存泄露吗?
不一定,如果父类的变量里面没有指针,没有开辟空间,都是普通的变量,就不会出现内存泄漏。
6、指针和引用的区别!!!!
(1) 引用必须定义时初始化,不能像指针一样仅int a;这样定义,必须int & b=a;
(2) int & const r = a;这样写错误,因为引用本身就不能改变指向,添加const多此一举。
(3) 指针可以有多级但引用只能有一级。有int ** ,但是没有int &&.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值