一、一个C++源文件从文本到可执行文件的过程?
通常来说一个C++源文件到可执行文件需要经过四个步骤:
(1)预处理,产生.ii文件
(2)编译,产生汇编文件(.s文件)
(3)汇编,产生目标文件(.o或.obj文件)
(4)链接,产生可行执行文件(.out或.exe文件)
(1)在预处理阶段主要执行以下操作:
a.对所有的“#define”进行宏展开;
b.处理所有的条件编译指令,比如“#if”,“#ifdef”,“#elif”,“#else”,“#endif”
c.处理“#include”指令,这个过程是递归的,也就是说被包含的文件可能还包含其他文件
d.删除所有的注释“//”和“/**/”
e.添加行号和文件标识
f.保留所有的“#pragma”编译器指令
经过预处理后的.ii文件不包含任何宏定义,因为所有的宏已经被展开,并且包含的文件也已经被插入到.ii文件中。
(2)在编译阶段主要就是将预处理完的文件进行一系列词法分析,语法分析,语义分析及优化后生成相应的汇编代码文件(.s文件)
(3)在汇编阶段,主要是将汇编代码转变成机器可以执行的代码,每一个汇编语句几乎都对应一条机器指令。最终产生目标文件(.o或.obj文件)。
(4)链接的过程主要包括了地址和空间分配(Address and Storage Allocation)、符号决议(Symbol Resolution)和重定位(Relocation)
二、线程和进程的区别与联系
进程:是执行中一段程序,即一旦程序被载入到内存中并准备执行,它就是一个进程。进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位。
线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位。
线程与进程的区别主要体现在5个方面:
三、数组和指针的区别
指针可以随时指向任意类型的内存块,而数组可以在静态存储区被创建。数组和指针的区别主要体现在以下两个方面
1.修改内容的不同,数组可以通过下标操作对元素值进行修改,而指针不行。
2.所占字节数不同,sizeof(p)得到的是一个指针变量的字节数,而不是p所指的内存容量。
四、内存分配
bss段:bss段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。
bss是英文Block Started by Symbol的简称。
bss段属于静态内存分配。
data段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。
数据段属于静态内存分配。
text段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。
这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读(某些架构也允许代码段为可写,即允许修改程序)。
在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。
当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);
当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。
栈(stack): 栈又称堆栈,是用户存放程序临时创建的局部变量,
也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。
五、为什么通常来说栈比堆要快?
首先, 栈是本着LIFO原则的存储机制, 对栈数据的定位相对比较快速, 而堆则是随机分配的空间, 处理的数据比较多, 无论如何, 至少要两次定位.
其次, 栈是由CPU提供指令支持的, 在指令的处理速度上, 对栈数据进行处理的速度自然要优于由操作系统支持的堆数据.
再者, 栈是在一级缓存中做缓存的, 而堆则是在二级缓存中, 两者在硬件性能上差异巨大.
最后, 各语言对栈的优化支持要优于对堆的支持, 比如swift语言中, 三个字及以内的struct结构, 可以在栈中内联, 从而达到更快的处理速度.。
六、TCP三次握手和四次挥手
TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接,如图1所示。
(1)第一次握手:建立连接时,客户端A发送SYN包(SYN=j)到服务器B,并进入SYN_SEND状态,等待服务器B确认。
(2)第二次握手:服务器B收到SYN包,必须确认客户A的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器B进入SYN_RECV状态。
(3)第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据。
关闭连接采用四次挥手:
(1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。
(2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
(3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A。
(4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。
七、C++中Overload,Override,Overwrite的区别?
Overload(重载):在C++程序中,可以将语义、功能相似的几个函数用同一个名字表示,但参数或返回值不同(包括类型、顺序不同),即函数重载。
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。
Override(覆盖):是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字。
Overwrite(重写):是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
八、C++ 虚函数的实现
C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。
对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。 在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了 这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。
这里我们着重看一下这张虚函数表。在C++的标准规格说明书中说到,编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。 这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。
九、AVL树、红黑树、B/B+树和Trie树的比较
AVL树、红黑树、B/B+树和Trie树
stl中的map和set用的是红黑树。B+树一般用于数据库系统中做索引,Trie树用来做字符匹配,Windows对进程地址空间的管理用到了AVL树。
十、 C++编译器自动为类产生的四个缺省函数是什么?
默认构造函数、析构函数、拷贝构造函数、赋值函数
十一、请简述C/C++语言中栈空间和堆空间的主要区别(栈为什么比堆快)?
十二、memcpy和memmove的区别
十三、叙述一下static对变量、对函数、对内函数内部的变量的各种使用的区别
十四、C++中引用与指针的区别
引用与指针有着相同的地方,即指针指向一块内存,他的内容是所指内存的地址,引用是某块内存的别名。但是两者并非完全相同,他们之间也存在着差别,具体表现在以下几个方面:
1)从本质上讲,指针是存放变量地址的一个变量,在逻辑上是独立的,他可以被改变,即其所指向的地址可以被改变,其指向的地址中存放的数据也可以被改变。而引用只是一个别名,他必须被初始化,并且引用的对象在其整个生命周期是不能被改变的,即自始至终只能依附于同一个变量。
2)作为参数传递时,两者不同。指针是值传递,引用是址传递。
3)引用使用时不需要解引用,而指针需要解引用。
4)引用不可以为空,而指针可以为空
5)对引用使用sizeof得到的是变量的大小,而对指针进行sizeof操作得到的是指针本身大小。
十五、C++11新特性
final: 显式禁止类被继承
default:显式实现默认构造/析构函数
nullptr:强类型的空指针
auto:自动类型推导
constexpr:允许将变量声明为constexpr类型让编译器来验证变量的值是否是一个常量表达式
noexpect:声明函数不能抛出任何异常,若定义了noexpect的函数抛出异常,则程序终止
override:显式的在派生类中声明哪些虚函数需要重写,若未重写则编译器会报错
lambda表达式: 就地编写匿名函数(闭包)
可变参数模板:可以在模板参数列表里书写任意数量类型名
模板别名:使用C++的模板别名特性可以简化容器的类型声明
模板元编程:在编译期计算类型,根据模板参数推导返回值。
十六、可变参数模板
可变参数模板是模板编程时,模板参数的个数可变的情况。C++11之前,模板在声明时必须有固定数量的参数模板。C++11允许模板定义有任意类型任意数量的参数模板。