c++面试必问41题(四)

  1. 请你说说虚拟内存与物理内存
  1. 物理内存 以前,程序寻址用的都是物理地址。程序能寻址的范围是有限的。比如在 32 位平台下,寻址的范围是 2^32 也就是 4G。并且这是固定的,如果没有虚拟内存,且每次开启一个进程都给 4G 物理内存,就可能会出现很多问题
  2. 虚拟内存 由于物理内存有很多问题,所以出现了虚拟内存。虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。
  1. 请你说说分段和分页

1. 分段

将用户程序地址空间分成若干个大小不等的段,每段可以定义一组相对完整的逻辑信息。存储分配时,以段为单位,段与段在内存中可以不相邻接,实现了离散分配。分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。

2. 分页

用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。分页主要用于实现虚拟内存,从而获得更大的地址空间。

3. 段页式

段页式存储管理方式即先将用户程序分成若干个段,再把每个段分成若干个页,并为每一个段赋予一个段名。在段页式系统中,为了实现从逻辑地址到物理地址的转换,系统中需要同时配置段表和页表,利用段表和页表进行从用户地址空间到物理内存空间的映射。

  1. 请你说说红黑树的特性,为什么要有红黑树

平衡树要求每个节点的左子树和右子树的高度差至多等于1,这个要求实在是太严了,导致每次进行插入/删除节点的时候,几乎都会破坏平衡树这个个规则,进而我们都需要通过左旋和右旋来进行调整,使之再次成为一颗符合要求的平衡树。显然,如果在那种插入、删除很频繁的场景中,平衡树需要频繁着进行调整,这会使平衡树的性能大打折扣,为了解决这个问题,于是有了红黑树,红黑树具有如下特点:

 1、具有二叉查找树的特点;

 2、根节点是黑色的;

 3、每个叶子节点都是黑色的空节点(NIL),也就是说,叶子节点不存数据;

 4、任何相邻的节点都不能同时为红色,也就是说,红色节点是被黑色节点隔开的;

 5、每个节点,从该节点到达其可达的叶子节点是所有路径,都包含相同数目的黑色节点。

  1. 请你说说什么是孤儿进程,什么是僵尸进程,如何解决僵尸进程
  1. 孤儿进程是指一个父进程退出后,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程号为1)所收养,并且由 init 进程对它们完整状态收集工作,孤儿进程一般不会产生任何危害。
  2. 僵尸进程是指一个进程使用 fork() 函数创建子进程,如果子进程退出,而父进程并没有调用 wait() 或者waitpid() 系统调用取得子进程的终止状态,那么子进程的进程描述符仍然保存在系统中,占用系统资源,这种进程称为僵尸进程。
  3. 为了防止产生僵尸进程,在 fork() 子进程之后我们都要及时在父进程中使用 wt() 或者 wtpid() 系统调用,等子进程结束后,父进程回收子进程 PCB 的资源。 同时,当子进程退出的时候,内核都会给父进程一个 SIGCHLD 信号,所以可以建立一个捕获 SIGCHLD 信号的信号处理函数,在函数体中调用 wt() 或 wtpid(),就可以清理退出的子进程以达到防止僵尸进程的目的。
  1. 请你说说 static 关键字的作用
  1. 限制数据的作用域(隐藏) 所有没有加 static 的全局变量和函数都具有全局可见性,其它源文件中也可以访问。被 static 修饰的全局变量和函数只能在当前源文件中访问,其它源文件访问不了,利用这个特性可以在不同的文件中定义同名变量和同名函数,而不必担心命名冲突。
  2. 延长数据的生命周期 普通的局部变量出了作用域就会释放,而静态变量存储在静态区,知道程序运行结束才会释放。
  3. 静态成员被该类所有对象共享 static 关键字可以修饰类中的成员变量和成员方法,被称为静态成员变量和静态成员方法,静态成员拥有一块单独的存储区,不管创建多少个该类的对象,所有对象都共享这一块内存。静态成员函数中不能访问普通的成员变量,只能访问静态成员变量,并且在静态成员函数中没有 this 指针(this是对象指针,静态成员函数不属于任何一个对象)。
  1. 请你说说什么是野指针,怎么产生的,如何避免
  1. 野指针是指指向的位置是随机的、不可知的、不正确的。
  2. 野指针产生的原因

a. 指针变量未初始化或者随便赋值:指针变量没有初始化,其值是随机的,也就是指针变量指向的是不确定的内存,如果对它解除引用,结果是不可知的。

b. 指针释放后未置空:有时候指针在释放后没有复制为 nullptr,虽然指针变量指向的内存被释放掉了,但是指针变量中的值还在,这时指针变量就是指向一个未知的内存,如果对它解除引用,结果是不可知的。

c. 指针操作超出了变量的作用域:函数中返回了局部变量的地址或者引用,因为局部变量出了作用域就释放了,这时候返回的地址指向的内存也是未知的。

3. 如何避免野指针

a. 指针变量一定要初始化,可以初始化为 nullptr。

b. 释放后置为 nullptr。

  1. 请你说说const 和 define 的区别

const用于定义常量;而define用于定义宏,而宏也可以用于定义常量。都用于常量定义时,它们的区别有:

1.const生效于编译的阶段;define生效于预处理阶段。

2.const定义的常量,在C语言中是存储在内存中、需要额外的内存空间的;define定义的常量,运行时是直接的操作数,并不会存放在内存中。

3.const定义的常量是带类型的;define定义的常量不带类型。因此define定义的常量不利于类型检查。

  1. 请你说说C++ 的内存管理

C++ 的内存分区主要有:代码区、未初始化数据区(BSS)、已初始化数据区(DATA)、栈区(Stack)、堆区(Heap) 1. 代码区 加载的是可执行文件代码段,所有的可执行代码都加载到代码区,这块内存是不可以在运行期间修改的。 2. 未初始化数据区 加载的是可执行文件 BSS 段,位置可以分开也可以紧靠数据段,存储于数据段的数据(全局未初始化,静态未初始化数据)的生存周期为整个程序运行过程。

  1. 已初始化数据区(全局初始化数据区/静态数据区) 加载的是可执行文件数据段,存储于数据段(全局初始化,静态初始化数据,文字常量(只读))的数据的生存周期为整个程序运行过程。
  2. 栈区 栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。
  3. 堆区 堆是一个大容器,它的容量要远远大于栈,但没有栈那样先进后出的顺序,用于动态内存分配。
  1. 请你说说内联函数和函数的区别,内联函数的作用
  1. 内联函数和函数的区别: - 内联函数比普通函数多了关键字 inline; - 内联函数避免了函数调用的开销;普通函数有调用的开销; - 普通函数在被调用的时候,需要寻址(函数入口地址);内联函数不需要寻址; - 内联函数有一定的限制,内联函数体要求代码简单,不能包含复杂的结构控制语句,如果内联函数函数体过于复杂,编译器将自动把内联函数当成普通函数来执行;普通函数没有这个要求。
  2. 内联函数的作用: 因为函数调用时候需要创建时间、参数传入传递等操作,造成了时间和空间的额外开销。通过编译器预处理,在调用内联函数的地方将内联函数内的语句复制到调用函数的地方,也就是直接展开代码执行,从而提高了效率,减少了一些不必要的开销。

40、请你说说使用指针需要注意什么

定义指针时,先初始化为NULL。

用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。

避免数字或指针的下标越界,特别要当心发生“多1”或者“少1”操作

动态内存的申请与释放必须配对,防止内存泄漏

用free或delete释放了内存之后,立即将指针设置为NULL,防止“野指针”

41、 请介绍一下5种IO模型

阻塞IO调用者调用了某个函数,等待这个函数返回,期间什么也不做,不停的检查这个函数有没有返回,必须等这个函数返回后才能进行下一步动作。

非阻塞IO非阻塞等待,每隔一段时间就去检查IO事件是否就绪。没有就绪就可以做其他事情。

信号驱动IOLinux用套接口进行信号驱动IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO事件就绪,进程收到SIGIO信号,然后处理IO事件。

IO多路复用:Linux用select/poll函数实现IO复用模型,这两个函数也会使进程阻塞,但是和阻塞IO所不同的是这两个函数可以同时阻塞多个IO操作。而且可以同时对多个读操作、写操作的IO函数进行检查。知道有数据可读或可写时,才真正调用IO操作函数。

异步IOLinux中,可以调用aio_read函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方式,然后立即返回,当内核将数据拷贝到缓冲区后,再通知应用程序。用户可以直接去使用数据。

42 说说 Cookie Session 的关系和区别是什么

Cookie与Session都是会话的一种方式。它们的典型使用场景比如“购物车”,当你点击下单按钮时,服务端并不清楚具体用户的具体操作,为了标识并跟踪该用户,了解购物车中有几样物品,服务端通过为该用户创建Cookie/Session来获取这些信息。

cookie数据存放在客户的浏览器上,session数据放在服务器上。

cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗 考虑到安全应当使用session。

session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能 考虑到减轻服务器性能方面,应当使用COOKIE。

单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

43 说说线程间通信的方式有哪些?

线程间的通信方式包括临界区、互斥量、信号量、条件变量、读写锁:

临界区:每个线程中访问临界资源的那段代码称为临界区(Critical Section)(临界资源是一次仅允许一个线程使用的共享资源)。每次只准许一个线程进入临界区,进入后不允许其他线程进入。不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它进行访问。

互斥量:采用互斥对象机制,只有拥有互斥对象的线程才可以访问。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。

信号量:计数器,允许多个线程同时访问同一个资源。

条件变量:通过条件变量通知操作的方式来保持多线程同步。

读写锁:读写锁与互斥量类似。但互斥量要么是锁住状态,要么就是不加锁状态。读写锁一次只允许一个线程写,但允许一次多个线程读,这样效率就比互斥锁要高。

44 说说线程同步方式有哪些?

线程间的同步方式包括互斥锁、信号量、条件变量、读写锁:

互斥锁:采用互斥对象机制,只有拥有互斥对象的线程才可以访问。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。

信号量:计数器,允许多个线程同时访问同一个资源。

条件变量:通过条件变量通知操作的方式来保持多线程同步。

读写锁:读写锁与互斥量类似。但互斥量要么是锁住状态,要么就是不加锁状态。读写锁一次只允许一个线程写,但允许一次多个线程读,这样效率就比互斥锁要高。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值