一 C与C++的区别
1.C是面向过程的语言,而C++是面向对象的语言,因此C++语言中有类和对象以及继承、多态这样的OOP语言必备的内容,此外C++支持模板,运算符重载,异常处理机制,以及一个非常强大的C++标准模板库STL
2.C只能写面向过程的代码,而C++既可以写面向过程的代码,也可以实现面向对象的代码;既然C++是面向对象的OOP语言,因此它还有非常强大的设计模式,比如单例,工厂,观察者模式等等,这些在C语言当中都是不支持的。
3.C和C++一个典型的区别就在动态内存管理上了,C语言通过malloc和free来进行堆内存的分配和释放,而C++是通过new和delete来管理堆内存的
4.另外强制类型转换上也不一样,C的强制类型转换使用()小括号里面加类型进行类型强转的,而C++有四种自己的类型强转方式,分别是const_cast,static_cast,reinterpret_cast和dynamic_cast
5.C++还支持带有默认值的函数,函数的重载,inline内联函数,这些C语言都不支持,当然还有const这个关键字,C和C++也是有区别的,但是这都是目前最常用的C89标准的C内容,在C99标准里面,C语言借鉴了C++的const和inline关键字,这两方面就和C++一样了。
6.由于C++多了一个类,因此和C语言的作用域比起来,就多了一个类作用域,此外,C++还支持namespace名字空间,可以让用户自己定义新的名字空间作用域出来,避免全局的名字冲突问题。
7.C++不仅支持指针,还支持更安全的引用,不过在汇编代码上,指针和引用的操作是一样的
8.由于C++是面向对象的语言,支持类对象,类和类之间的代理,组合,继承,多态等等面向对象的设计,有很多的设计模式可以直接使用,因此在设计大型软件的时候,通常都会采用面向对象语言,而不会采用面向过程语言,可以更好的进行模块化设计,做到软件设计的准则:高内聚,低耦合!
9.在C++中,struct关键字不仅可以用来定义结构体,它也可以用来定义类
指针与引用的区别:
4:指针存在常量指针int const *p和指针常量int *const p,而引用只存在常量引用int const &a,不存在引用常量int& const a。
8进行++与赋值操作的时候,有不同。(第四点不要说)
二 编译链接过程
编译过程主要分为预处理,编译,汇编,生成二进制可重定位目标文件(.obj文件).obj文件的组成。
预编译主要的工作就是处理源代码文件中以’#'开头的预编译指令:比如#define删除,并展开宏定义;#include指令,将被包含的文件插入到预编译指令的位置;删除注释;添加行号和文件名标识
编译主要的工作进行一系列的词法解析、语法解析、语义分析以及源代码优化,汇总符号,生成相应的汇编指令
汇编把汇编代码转换为机器可以执行的指令
链接过程。除了所有的obj文件,还有哪些东西要参与链接过程,链接过程分为两步,第一步进行各个段合并,调整段的大小和偏移量,符号表合并进行符号解析(符号解析做什么事情?):所有文件对符合引用的地方都要找到符号定义的地方,生成符号虚拟地址;第二步是进行符号重定向(能不能解释下符号重定向表示什么意思?),最终生成可执行文件,可执行文件的组成格式是什么?和obj文件的组成格式比较,有什么不一样的地方呢?
三 C与C++的内存布局
五函数调用过程
int sum(int a,int b)
{
左括号 // push ebp
// mov ebp esp
// sub esp 04Ch rep stos
for
int tmp=0;
// mov dword ptr[ebp-4] ,0
tmp=a+b;
//mov eax dword ptr[ebp+0Ch]
// add eax dword ptr[ebp+8]
//mov dword ptr[ebp-4], eax
return tmp;
// mov eax dword ptr[ebp-4]
} 右括号 //mov esp ebp
// pop ebp 出栈并把值赋给ebp
//ret 出栈,把出栈的内容放入CPU的PC寄存器中
//执行PC寄存器中的指令
int main()
{
int a=10; mov dword ptr[ebp-4] ,04h;
int b=20; mov dword ptr[ebp-8] ,14h;
int ret=sum(a,b);
//压栈 mov eax dword ptr[ebp-8]
push eax
mov eax dword ptr[ebp-4]
push eax
// call sum push 下一行指令的地址
下一行指令// 0x08124458 add esp ,8
// mov dword ptr[ebp-0Ch], eax 把传递出来的值赋值给ret
cout<<"ret="<<ret<<endl;
return 0;
}
六new与malloc的区别
首先new与delete是c++的关键字;malloc与free是C语言的标准库函数;都可以用于内存的申请与释放;
malloc申请的空间没有明确类型,需要我们进行类型强转,需要我们计算所需字节数;而new指定了数据类型,自动计算所需要空间大小;
malloc申请空间的值是随机值;new申请的同时可以初始化;
申请失败时,malloc返回空,new抛出异常;
malloc开辟的内存永远是通过free来释放的;而new 单个元素内存,用的是delete;new[]数组,用的是delete[]释放;
malloc开辟内存只有一种方式,而new有四种,普通的new(开辟失败抛出bad-alloc异常),nothrow的new,const new以及定位new
七普通函数与内联函数与宏的区别
内联函数和宏很类似,而区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的,而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。
普通函数的调用在汇编上有标准的push压实参指令,然后call指令调用函数,给函数开辟栈帧,函数运行完成,有函数退出栈帧的过程;而inline内联函数是在编译阶段,在函数的调用点将函数的代码展开,省略了函数栈帧开辟回退的调用开销,效率比较高!内联函数不宜含有循环 递归,内联函数是对编译器的一个建议,具体是否设置为内联函数要看编译器。
release版本中两者都需要栈帧的开辟与回退,内联函数不生成符号。
八 四种指针强转类型
注:
顶层const:表示指针本身是个常量。如:int *const p;
底层const:表示指针所指的对象是一个常量。如:int const *p;
const_cast :一般用来去掉const属性
reinterpret_cast:类似于C的强转,任何指针都可以转换成其他类型的指针(限制了整形 浮点型 枚举类型之间的相互转换;整形与字符类型的转换),不安全
static_cast:更安全的类型强转,只能内置类型之间相互转换,对于类只能在有联系的指针之间进行转换。不包含底层const;
dynamic_cast:通常在基类与派生类之间进行转换使用。运行时类型识别(以区别以上三个均在编译时识别),用于将基类的指针或引用安全地转换成派生类的指针或引用。
对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针;
对引用进行dynamic_cast,失败抛出一个异常bad_cast,成功返回正常cast后的对象引用。
对于“向上转换”(即派生类指针或引用类型转换为其基类类型),无论是指针还是引用向上转换都是安全地。
对于“向下转型”有两种情况:
一种是基类指针所指对象是派生类类型的,这种转换是安全的;
另一种是基类指针所指对象为基类类型,在这种情况下dynamic_cast在运行时做检查,转换失败,返回结果为0;
在引用上,dynamic_cast依旧是常用于“安全的向下转型”。与指针一样,引用的向下转型也可以分为两种情况,与指针不同的是,并不存在空引用,所以引用的dynamic_cast检测失败时会抛出一个bad_cast异常。
九 智能指针
智能指针的引进是一种是为了解决堆区资源忘记释放而造成的内存泄漏问题;;一种是尚有指针引用内存的情况下就释放了它,就会产生引用非法内存的指针。智能指针其实是一个类,类中含有一个指针指向堆区资源。通过在栈上实例化智能指针对象从而使得所申请的堆区资源出了作用域后能够自动释放。
STL 一共给我们提供了四种智能指针:auto_ptr、unique_ptr、shared_ptr 和 weak_ptr,auto_ptr 是 C++98 提供的解决方案,C+11 已将其摒弃,并提出了 unique_ptr 作为 auto_ptr 替代方案。shared_ptr 和 weak_ptr 则是 C+11 从准标准库Boost中引入的两种智能指针。此外,Boost库还提出了 boost::scoped_ptr
智能指针可以分为带引用计数的智能指针,不带引用计数的智能指针。不到引用计数的智能指针有:auto_ptr ,scoped_ptr,unique_ptr;带引用计数的智能指针 shared_ptr,weak_ptr;
auto_ptr: 进行拷贝构造的时候,会把当前指针所指的资源赋给新的指针,同时当前指针置空,即一个资源只能一个指针所拥有;以前的指针访问会出错。
scoped_ptr:scoped_ptr将拷贝构造函数与赋值重载函数进行了限制,使得两个函数被调用将会报错。
unique_ptr:与auto_ptr类似,是对auto_ptr的一个改进,同时unique_ptr也不支持拷贝构造与赋值重载函数,对此进行了限定,但unique_ptr对右值引用的指针同样会进行一个资源的转移,但该过程是我们所希望的;能够大大减少我们再次使用以前的原指针所发生的的错误
shared_ptr:带引用计数的强智能指针。智能指针类中,除了一个指针指向堆区资源,还有一个指针指向引用计数类,记录资源的被引用个数。当有新的指针指向资源时,引用计数加一;有指针撤销指向时,引用计数减一;引用计数为0时,释放资源。支持拷贝构造与赋值重载,析构函数必须引用计数减为0才进行资源的释放。
问题:shared_ptr的使用会出现一个问题,智能指针的交叉引用,从而造成的资源的无法释放,内存泄漏问题。为了解决这一问题,提出了weak_ptr:
weak_ptr:weak_ptr的使用不对引用计数发生改变,更多的是一种观察者,因为没有引用计数,所以资源存在还是已经被释放,weak_ptr不能确定。weak_ptr可以升级为shared_ptr,(lock()方法);同样的,因为资源不一定存在,升级可能失败,返回空,成功则返回一个shared_ptr。
十 构造函数为什么不能是虚函数,还有哪些函数不能是虚函数
1.虚函数的地址要存在虚函数表当中,因此必须要能够产生函数符号地址,因此inline函数就不能成为virtual函数
2.只能通过对象的前四个字节vfptr才能访问虚函数表,进而访问虚函数的地址,虚函数必须依赖于对象
基于以上两点如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,无法找到vtable,所以构造函数不能是虚函数。
3 虚函数主要用于在信息不全的情况下,能使函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。
inline函数、static修饰的函数、普通函数、友元函数(不属于类内函数)、构造函数
十一:库函数与系统调用函数的区别:
系统调用:是操作系统为用户提供的一系列操作的接口,这些接口提供了对系统硬件设备功能的操作,是通向操作系统本身的接口,是面向底层硬件的。通过系统调用,可以使得用户态运行的进程与硬件设备(如CPU、磁盘、打印机等)进行交互,是操作系统留给应用程序的一个接口。
库函数(Library function)是把函数放到库里,供别人使用的一种方式。某些功能模块的集合,项目之间代码的共享,模块化开发。预先编译好的方法的集合。库函数有可能包含有一个系统调用,有可能有好几个系统调用,当然也有可能没有系统调用
区别:(1)库函数是语言或应用程序的一部分,而系统调用是内核提供给应用程序的接口,操作系统的入口点,属于系统的一部分
(2)库函数在用户地址空间执行,系统调用是在内核地址空间执行,库函数运行时间属于用户时间,系统调用属于系统时间,库函数开销较小,系统调用开销较大
(3)库函数是有缓冲的,系统调用是无缓冲的
(4)系统调用依赖于平台,库函数并不依赖
(5)函数库的调用是调用库的一段程序,而系统调用时调用系统内核的服务
十二:系统调用的过程:
应用程序在用户态系统调用时,触发0x80终端,进入内核态,cpu进入内核态,执行系统调用总控程序,把应用程序的一些信息:寄存器 进程打开的文件、数据、用户堆栈放在内核空间的堆栈区中,保存用户上下文信息(保护现场);把系统调用的参数放入寄存器中,根据eax寄存器的值查找系统调用表,找到对应的系统调用函数地址,执行,把值通过eax寄存器返回,继续执行系统调用总控程序,用户态信息的回复,重新进入用户态。
十三:哈希冲突、如何解决哈希冲突、负载均衡中的普通哈希、一致性哈希
哈希冲突:key值不同,但经过哈希函数得到的哈希值是相同的,不同的元素存储在同一个地址空间上。
哈希函数:直接定址法、除留取余法、数字分析法、平方取中法、随机数法、折叠法
A. 直接定址法:取关键字的线性函数值作为哈希地址。
B. 数字分析法:取关键字的中的若干位作为哈希地址。
C. 平方取中法:取关键字平方后的中间几位作为哈希地址。
D. 折叠法:将关键字分割成位数相同的几部分(最后一部分可以不同),然后取这几部分的叠加和作为哈希地址。
E. 除留余数法:H(key) = key MOD m ,m为不大于哈希表长度的数。
F. 随机函数法
解决方法(只能减少哈希冲突,无法避免):开放地址法(线性探测法、二次探测法、伪随机探测法)在散列、链地址法、建立公共溢出区
十四:地址映射的过程:
实模式下:
段基址+ IP(偏移量/逻辑地址)=数据地址(物理地址)
保护模式:
十五:缺页中断与缺页异常