面试整理要背的

一.c\c++语言部分

1. 在C语言中,关键字static有三个明显的作用:

A、一旦声明为静态变量,在编译时刻开始永远存在,不受作用域范围约束,但是如果是局部静态变量,则此静态变量只能在局部作用域内使用,超出范围不能使用,但是它确实还占用内存,还存在.
B、在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
C、在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

2. 关键字volatile有什么含意?并给出三个不同的例子。

定义为volatile的变量表明变量可能会被意想不到地改变,编译器就不会去假设这个变量的值。准确地说,优化器在用到volatile修饰的变量时必须每次都小心地重新读取变量的值,而不是使用保存在寄存器里的备份。
使用volatile变量的例子:
A、并行设备的硬件寄存器(如:状态寄存器)
B、一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
C、多线程应用中被几个任务共享的变量

深入理解:

(1)一个参数既可以是const还可以是volatile吗?解释为什么。
是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
(2)一个指针可以是volatile 吗?解释为什么。
是的。尽管这并不很常见。一个例子是当一个中服务子程序修改一个指向一个buffer的指针时。

3.引用和指针有什么区别?

A、应用必须初始化,指针不必;

B、引用处画化后不能改变,指针可以被改变;

C、不存在指向空值的引用,但存在指向空值的指针;

4.h头文件中的ifndef/define/endif 的作用?
答:防止该头文件被重复引用。
5.程序内存分配

A、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
B、堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
C、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
D、文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。
E、程序代码区—存放函数体的二进制代码

这是一个前辈写的,非常详细
//main.cpp
  int a=0;    //全局初始化区
  char *p1;   //全局未初始化区
  main()
  {
   intb;栈
   char s[]="abc";   //栈
   char *p2;         //栈
   char *p3="123456";   //123456\0在常量区,p3在栈上。
   static int c=0;   //全局(静态)初始化区
   p1 = (char*)malloc(10);
   p2 = (char*)malloc(20);   //分配得来得10和20字节的区域就在堆区。
   strcpy(p1,"123456");   //123456\0放在常量区,编译器可能会将它与p3所向"123456"优化成一个地方。
}

6.typedef和define和const的区别

typedef是C语言中用来声明自定义数据类型,配合各种原有数据类型来达到简化编程的目的的类型定义关键字。 #define是预处理指令,是宏定义

7.单元测试

  • 单元测试:  单元测试是对软件基本组成单元(软件设计的最小单位)进行正确性检验的测试工作,如函数、过程(function,procedure)或一个类的方法(method)。

  • 集成测试:  集成测试是在单元测试的基础上,将所有模块按照概要设计要求组装成为子系统或系统,验证组装后功能以及模块间接口是否正确的测试工作。集成测试也叫组装测试、联合测试、子系统测试或部件测试。

  • 系统测试:  系统测试是将经过集成测试的软件,作为计算机系统的一个部分,与系统中其他部分结合起来,在实际运行环境下对计算机系统进行的一系列严格有效地测试,以发现软件潜在的问题,保证系统的正常运行。

8. const常量

  • const 声明的变量只读
  • const 一定要要赋予初值
  • const 修饰的成员函数不可以改变
  • const 修饰的类只能够调用const的成员函数
  • const 指针
  • const优于define 因为有安全性检查

9.sizeof和strlen区别

(1)sizeof是一个操作符,strlen是库函数;
(2)sizeof的参数可以是数据的类型,也可以是变量,而strlen只能以结尾为’\0’的字符串做参数;
(3)编译器在编译时就计算出了sizeof的结果。而strlen函数必须在运行时才能计算出来。并且sizeof计算的是数据类型占内存的大小,而strlen计算的是字符串实际的长度;
(4)数组做sizeof的参数不退化,传递给strlen就退化为指针

10.memcpy和strcpy的区别

(1)操作对象不同:
strcpy的两个操作对象均是字符串
sprintf的操作源对象可以是多种数据类型,目的操作对象是字符串
memcpy的两个对象就是两个人一可操作的内存地址,不限于何种数据类型。
(2)执行效率不同:
memcpy最高,strcpy次之,sprintf效率最低。
(3)实现功能不同:
strcpy主要实现字符串变量间的拷贝
sprintf主要实现其他数据类型格式到字符串的转化
memcpy主要是内存块间的拷贝

11.面向对象的三大特征
面向对象的三大特征:封装性,继承性,多态性
12 malloc和new的区别

①、malloc/ free是 C++/C语言的标准库函数,而new/ delete是C++的运算符。
②、malloc内存分配成功返回的类型为void*,需要通过强制类型转换将void*转换为我们需要的类型。
③、new内存分配失败时会抛出bac_alloc异常,不会返回NULL;而malloc分配失败时则返回NULL。
④、使用new操作符申请内存分配是无需指定内存块的大小,而malloc则需要显式地指出所需的内存大小。

13C语言的关键字static和C++的关键字static有什么区别?

在C中static用来修饰局部静态变量和外部静态变量、函数。
而C++中除了以上功能之外,还可以用来定义类的成员变量和函数。即静态成员和静态成员函数。

编程时static的记忆性和全局性的特点可以让在不同时期调用的函数进行通信,传递信息,而C++的静态成员则可以在多个对象实例间进行通信,传递信息。

14 .变量的声明和定义有什么区别?

为变量分配地址和存储空间的称为定义,不分配地址的称为声明。一个变量可以在多个地方声明,但是只能在一个地方定义。加入extern修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。

15.简述多态实现的原理

编译器发现一个类里面有虚函数,便会立即为此类生成虚函数表vtable,虚函数表的各项为指向对应虚函数的指针。编译器还会为此类中隐含插入一个指针vptr(对vc编译器会插在第一个位置),指向虚函数表。调用此类的构造函数时,在类的构造函数中,编译器会隐含执行vptr和vtable的关联代码,将vptr指向相应的vtable,将类与此类的vtable联系起来,另外在调用类的构造函数时,指向基础类的指针此时已经变成指向具体类的this指针,这样依靠this就可以指向vtable ,如此才能够真正与函数进行链接,这就是动态的联编,实现多态的基本原理-----虚函数是多态的基础

16.谈谈对面向对象的认识

面向对象可以理解成对待每一个问题,都是首先要确定这个问题由几个部分组成,而每一个部分其实就是一个对象。然后再分别设计这些对象,最后得到整个程序。传统的程序设计多是基于功能的思想来进行考虑和设计的,而面向对象的程序设计则是基于对象的角度来考虑问题。这样做能够使得程序更加的简洁清晰。
说明:编程中接触最多的“面向对象编程技术”仅仅是面向对象技术中的一个组成部分。发挥面向对象技术的优势是一个综合的技术问题,不仅需要面向对象的分析,设计和编程技术,而且需要借助必要的建模和开发工具。

17.构造函数能否成为虚函数

  • 构造函数不能是虚函数。而且不能在构造函数中调用虚函数,因为那样实际执行的是父类的对应函数,因为自己还没有构造好。
  • 析构函数可以是虚函数,而且,在一个复杂类结构中,这往往是必须的。析构函数也可以是纯虚函数,但纯虚析构函数必须有定义体,因为析构函数的调用是在子类中隐含的。
  • 说明:虚函数的动态绑定特性是实现重载的关键技术,动态绑定根据实际的调用情况查询相应类的虚函数表,调用相应的虚函数

18野指针产生的原因和避免方法

  • 情况一
    原因 指针变量声明时没有被初始化。
    解决办法 指针声明时初始化,可以是具体的地址值,也可让它指向NULL。
  • 情况二
    原因 指针 p 被 free 或者 delete 之后,没有置为 NULL。
    解决办法 指针指向的内存空间被释放后指针应该指向NULL。
  • 情况三
    原因 指针操作超越了变量的作用范围。
    解决办法 在变量的作用域结束前释放掉变量的地址空间并且让指针指向NULL。
  • 注意 “野指针”的解决方法也是编程规范的基本原则,平时使用指针时一定要避免产生“野指针”,在使用指针前一定要检验指针的合法性.

19.常考代码

1.排序:快拍,冒泡,shell
2.单双链表:
3.计算二叉树的深度
4.二叉树的增删查改
5.查找数组里面第几大的数
6.判断字符串是否相等
7.字符串的复制

20.链表和数组什么区别

>指针和数组区别
  • 数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。
    用运算符sizeof 可以计算出数组的容量(字节数)。sizeof§,p 为指针得到的是一个 指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。

数组和链表有以下不同:
(1)存储形式:数组是一块连续的空间,声明时就要确定长度。链表是一块可不连续的动态空间,长度可变,每个节点要保存相邻结点指针;
(2)数据查找:数组的线性查找速度快,查找操作直接使用偏移地址。链表需要按顺序检索结点,效率低;
(3)数据插入或删除:链表可以快速插入和删除结点,而数组则可能需要大量数据移动;
(4)越界问题:链表不存在越界问题,数组有越界问题。

21、堆栈溢出一般是由什么原因导致的?
答 :1.没有回收垃圾资源
2.层次太深的递归调用

22、不能做switch()的参数类型
答 :switch的参数不能为实型(float double之类的)。

2.网络编程部分

1、TCP与UDP的区别

  • TCP:是面向连接的流传输控制协议,具有高可靠性,确保传输数据的正确性,有验证重发机制,不会出现丢失或乱序。

  • UDP:是无连接的数据报服务,不对数据报进行检查与修改,无须等待对方的应答,会出现分组丢失、重复、乱序,但具有较好的实时性,UDP段结构比TCP的段结构简单,因此网络开销也小
    2、流量控制和拥塞控制

  • 拥塞控制
    网络拥塞现象是指到达通信子网中某一部分的分组数量过多,使得该部分网络来不及处理,以致引起这部分乃至整个网络性能下降的现象,严重时甚至会导致网络通信业务陷入停顿,即出现死锁现象。拥塞控制是处理网络拥塞现象的一种机制。

  • 流量控制
    数据的传送与接收过程当中很可能出现收方来不及接收的情况,这时就需要对发方进行控制,以免数据丢失。

3. tcp连接建立的时候3次握手,断开连接的4次握手的具体过程

  • 建立连接采用的3次握手协议,具体是指:
    第一次握手是客户端connect连接到server,server accept client的请求之后,向client端发送一个消息,是第二次握手,第3次握手就是client向server发送的,就是对第二次握手消息的确认。之后client和
    server就开始通讯了。
  • 断开连接的4次握手,具体如下:
    断开连接的一端发送close请求是第一次握手,另外一端接收到断开连接的请求之后需要对close进行确认,发送一个消息,这是第二次握手,发送了确认
    消息之后还要向对端发送close消息,要关闭对对端的连接,这是第3次握手,而在最初发送断开连接的一端接收到消息之后,进入到一个很重要的状态
    time_wait状态,最后一次握手是最初发送断开连接的一端接收到消息之后对消息的确认。

4、epoll与select的区别

  • select在一个进程中打开的最大fd是有限制的,由FD_SETSIZE设置,默认值是2048。不过
  • epoll则没有这个限制,它所支持的fd上限是最大可以打开文件的数目,这个数字一般远大于2048,一般来说内存越大,fd上限越大,1G内存都能达到大约10w左右。
  • select的轮询机制是系统会去查找每个fd是否数据已准备好,当fd很多的时候,效率当然就直线下降了,epoll采用基于事件的通知方式,一旦某个fd数据就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,而不需要不断的去轮询查找就绪的描述符,这就是epoll高效最本质的原因。
  • 无论是select还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的,而select则做了不必要的拷贝.

5、epoll中et和lt的区别与实现原理

  • LT:水平触发,效率会低于ET触发,尤其在大并发,大流量的情况下。但是LT对代码编写要求比较低,不容易出现问题。LT模式服务编写上的表现是:只要有数据没有被获取,内核就不断通知你,因此不用担心事件丢失的情况。
  • ET:边缘触发,效率非常高,在并发,大流量的情况下,会比LT少很多epoll的系统调用,因此效率高。但是对编程要求高,需要细致的处理每个请求,否则容易发生丢失事件的情况。

6、多线程如何同步

Linux系统中多线程同步有最常用的是:互斥锁、条件变量和信号量。
https://blog.csdn.net/qq_17308321/article/details/79929623

4、进程间通讯的方式及优缺点

  • A、管道( pipe )
    管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
  • B、有名管道
    有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
  • C、信号量( semophore )
    信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
  • D、消息队列( message queue )
    消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  • E、信号 ( sinal )
    信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
  • F、共享内存( shared memory)
    共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是 针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
  • G、套接字( socket ) 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。

优缺点

  • A、无名管道简单方便,但局限於单向通信的工作方式,并且只能在亲缘进程之间实现管道的共享;有名管道虽然可以提供给任意关系的进程使用,但是由于其长期存在于系统之中,使用不当容易出错
  • B、消息队列可以不再局限于父子进程,而允许任意进程通过共享消息队列来实现进程间通信,并由系统调用函数来实现消息发送和接收之间的同步,从而使得用户在使用消息缓冲进行通信时不再需要考虑同步问题。使用方便,但是信息的复制需要额外消耗CPU的时间,不适宜于信息量大或操作频繁的场合。
  • C、共享内存针对消息缓冲的缺点改而利用内存缓冲区直接交换信息,无须复制,快捷、信息量大是其优点。但是共享内存的通信方式是通过将共享的内存缓冲区直接附加到进程的虚拟地址空间中来实现的。因此,进程之间的读写操作的同步问题操作系统无法实现,必须由各进程利用其他同步工具解决。另外,由于内存实体存在于计算机系统中,所以只能由处于同一个计算机系统中的诸进程共享,不能网络通信。

5.信号量

信号量是用来解决进程间的同步与互斥问题的一种进程间通信机制,包括一个称为信号量的变量和在该信号量下等待资源的进程等待队列,以及对信号量进行的两个原子操作(P/V操作)。其中,信号量对应于某一种资源,取一个非负的整形值。信号量值(常用sem_id表示)指的是当前可用的该资源的数量,若等于0则意味着目前没有可用的资源。

● P操作:如果有可用的资源(信号量值>0),则此操作所在的进程占用一个资源(此时信号量值减1,进入临界区代码);如果没有可用的资源(信号量值=0),则此操作所在的进程被阻塞直到系统将资源分配给该进程(进入等待队列,一直等到资源轮到该进程)。
● V操作:如果在该信号量的等待队列中有进程在等待资源,则唤醒一个阻塞进程;如果没有进程等待它,则释放一个资源(即信号量值加1)。

6.在浏览器中输入 www.baidu.com 后执行的全部过程*
7.TCP 的可靠性如何保证?
TCP的可靠性是通过顺序编号和确认(ACK)来实现的。
8.OSI

1、OSI的七层协议是
应用层、表示层、会话层、传输层、网络层、物理链路层、物理层
1.1、TCP/IP分层(4层)
链路层、网络层、运输层、应用层
1.2、五层协议(5层)
物理层、数据链路层、网络层、运输层、应用层

9、分页和分段有什么区别(内存管理)?

段式存储管理是一种符合用户视角的内存分配管理方案。在段式存储管理中,将程序的地址空间划分为若干段(segment),如代码段,数据段,堆栈段;这样每个进程有一个二维地址空间,相互独立,互不干扰。段式管理的优点是:没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)

页式存储管理方案是一种用户视角内存与物理内存相分离的内存分配管理方案。在页式存储管理中,将程序的逻辑地址划分为固定大小的页(page),而物理内存划分为同样大小的帧,程序加载时,可以将任意一页放入内存中任意一个帧,这些帧不必连续,从而实现了离散分离。页式存储管理的优点是:没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满)。

两者的不同点:

目的不同:分页是由于系统管理的需要而不是用户的需要,它是信息的物理单位;分段的目的是为了能更好地满足用户的需要,它是信息的逻辑单位,它含有一组其意义相对完整的信息;

大小不同:页的大小固定且由系统决定,而段的长度却不固定,由其所完成的功能决定;

地址空间不同: 段向用户提供二维地址空间;页向用户提供的是一维地址空间;

信息共享:段是信息的逻辑单位,便于存储保护和信息的共享,页的保护和共享受到限制;

内存碎片:页式存储管理的优点是没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满);而段式管理的优点是没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)。

10、什么是虚拟内存?
1).内存的发展历程
  没有内存抽象(单进程,除去操作系统所用的内存之外,全部给用户程序使用) —> 有内存抽象(多进程,进程独立的地址空间,交换技术(内存大小不可能容纳下所有并发执行的进程)
)—> 连续内存分配(固定大小分区(多道程序的程度受限),可变分区(首次适应,最佳适应,最差适应),碎片) —> 不连续内存分配(分段,分页,段页式,虚拟内存)
2).虚拟内存
  虚拟内存允许执行进程不必完全在内存中。虚拟内存的基本思想是:每个进程拥有独立的地址空间,这个空间被分为大小相等的多个块,称为页(Page),每个页都是一段连续的地址。这些页被映射到物理内存,但并不是所有的页都必须在内存中才能运行程序。当程序引用到一部分在物理内存中的地址空间时,由硬件立刻进行必要的映射;当程序引用到一部分不在物理内存中的地址空间时,由操作系统负责将缺失的部分装入物理内存并重新执行失败的命令。这样,对于进程而言,逻辑上似乎有很大的内存空间,实际上其中一部分对应物理内存上的一块(称为帧,通常页和帧大小相等),还有一些没加载在内存中的对应在硬盘上,如图5所示。
注意,请求分页系统、请求分段系统和请求段页式系统都是针对虚拟内存的,通过请求实现内存与外存的信息置换。
3). 页面置换算法

FIFO先进先出算法:在操作系统中经常被用到,比如作业调度(主要实现简单,很容易想到);

LRU(Least recently use)最近最少使用算法:根据使用时间到现在的长短来判断;

LFU(Least frequently use)最少使用次数算法:根据使用次数来判断;

OPT(Optimal replacement)最优置换算法:理论的最优,理论;就是要保证置换出去的是不再被使用的页,或者是在实际内存中最晚使用的算法。
4). 虚拟内存的应用与优点

虚拟内存很适合在多道程序设计系统中使用,许多程序的片段同时保存在内存中。当一个程序等待它的一部分读入内存时,可以把CPU交给另一个进程使用。虚拟内存的使用可以带来以下好处:

  • 在内存中可以保留多个进程,系统并发度提高
  • 解除了用户与内存之间的紧密约束,进程可以比内存的全部空间还大

11、颠簸

颠簸本质上是指频繁的页调度行为,具体来讲,进程发生缺页中断,这时,必须置换某一页。然而,其他所有的页都在使用,它置换一个页,但又立刻再次需要这个页。因此,会不断产生缺页中断,导致整个系统的效率急剧下降,这种现象称为颠簸(抖动)。

内存颠簸的解决策略包括:

  • 如果是因为页面替换策略失误,可以修改替换算法来解决这个问题;
  • 如果是因为运行的程序太多,造成程序无法同时将所有频繁访问的页面调入内存,则要降低多道程序的数量;
  • 否则,还剩下两个办法:终止该进程或增加物理内存容量。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章