C++面试题

C++中常见的面试题:

1、指针和数组的区别:

定义一个数组int arr[0 ] ={0};  arr[1] =20;

定义一个指针int a = 20; int *p = &a;

在汇编上:

int arr[10] = {0};

000813BE  mov         dword ptr [arr],0

000813C5  xor          eax,eax

000813C7  mov         dword ptr [ebp-28h],eax

000813CA  mov         dword ptr [ebp-24h],eax

000813CD  mov         dword ptr [ebp-20h],eax

000813D0  mov         dword ptr [ebp-1Ch],eax

000813D3  mov         dword ptr [ebp-18h],eax

000813D6  mov         dword ptr [ebp-14h],eax

000813D9  mov         dword ptr [ebp-10h],eax

000813DC  mov         dword ptr [ebp-0Ch],eax

000813DF  mov         dword ptr [ebp-8],eax

arr[2] = 20;

000813E2  mov         dword ptr [ebp-24h],14h   

指针:

   int a = 20;

012913BE  mov         dword ptr [a],14h

int *p =&a;

012913C5  lea          eax,[a]

012913C8  mov         dword ptr [p],eax

 2、指针与引用的区别:

在C语言里没有引用,在C++11以前只有一级引用,以后有左引用和右引用,int &s =a;    013013C5  lea         eax,[a]

                                                                                                                                           013013C8  mov         dword ptr [s],eax

在汇编上指针跟引用没有区别。但在使用上:使用引用变量会开辟内存但是没用过;引用变量在汇编上是指针;访问引用变量访问的是它所引用的内存,所以a = *p =s,指的是同一块内存;引用必须初始化,初始化的值必须取地址;引用能变;

2.函数的重载

定义:函数名相同,参数个数不同,不能仅根据返回值不同来区分函数是否重载;且要在同一个作用域,

void fun(int  a);

void fun(const int a );不能构成重载;

用指针和引用修饰才能构成重载。改成void fun(int *a);void fun(const int *a);因为C++在编译阶段会产生符号表,链接时要进行符号解析,符号解析完成后要给符号分配地址,因为C++编译函数产生的符号表是函数名+参数,所以同一个符号只能出现一次。在重载的过程中,首先会进行参数个数相同,同类型的匹配,如果没有会退化为内置类型的隐式转换,在没有就是找不到。

3.多态分为静多态和动多态

静多态是指编译时期的多态:函数的重载在编译时期已经确定要调用哪个函数;模板,在编译时期已经确认要实例化哪个模板;

动多态是指运行时的多态:主要指继承里面的虚函数,虚函数在编译时期会产生一个虚函数表,将函数的地址写在虚函数表里,当基类指针指向不同派生类对象,通过指针调用基类和派生类的同名覆盖方法,会发生多态,在链接的时候会将虚函数表加载到只读数据段(rdata)内;虚函数表与类型一一对应,运行时从vfptr找到vftable,从表里面取地址,指向谁就调用谁。

4.在函数调用的时候会出现绑定问题:

分为早绑定和晚绑定

早绑定就是静态绑定;也就是在编译时期 已经确认好要调用哪个函数:call  base::show addr

晚绑定就是动态的绑定,在运行时期:mov  eax,dword ptr[obj]

                                 mov  ecx,dword ptr[eax]

                                 call   ecx

因为call 寄存器 ,就看运行时寄存器里面的地址指向谁就说明调用哪个函数。

5、列举一些内存泄漏的原因:

内存只申请不释放,malloc完了之后忘记free,new之后忘了delete;

浅拷贝,或者在赋值运算符重载的时候,operator=,没有释放现有的内存,直接赋值;

智能指针的交叉使用;

基类指针指向在堆内生成的派生类对象,析构时;

New[]开辟了一段内存,用delete释放;

在open没有close,FILE* pf = open();

构造函数抛出异常,在抛出异常之前申请的内存没得到释放就泄露了;

Linux上,fd泄漏了,因为fd上限65535;

Linux上如果出现僵尸进程,会造成内核资源的泄漏;

例如:int *fun(){  int * p= new int ;    return p;}

在调用函数完成后要对资源进行释放;、

6、深究一下malloc 底层的实现:

Malloc ----- ptmalloc ---- (do_fork()/do_mmap);

Ptmalloc  ------  bins[128] -------- chunk;

画出虚拟地址空间、在gcc上的实现、数据结构、内核上;

7.STL里有哪些容器

顺序容器:Vector:  List : deque;

关联容器:set/multiset;map/multimap;hashmap;

容器适配器:stack (deque); queue(deque) ;priority_queue(vector大根堆);没有迭代器;

Reserve()/resize();

8.设计模式:单例模式

分析多线程是否安全的思路:

首先看进程的结果会不会随着线程调度顺序的不同而不同,如果会,看是不是存在竞态条件,把存在竞态条件的代码段称为临界区代码段,再看临界区代码段进行的是不是原子操作,如果不是就要加互斥锁。再就是你加了互斥锁以后,既要保证多线程的安全又要保证单线程的效率,所以进行二次判断。

工厂/抽象工厂模式

观察者模式

9、编译链接的原理:

预编译:处理所有以#开头的预编译指令、以及注释

编译:进行语法检查,代码优化,符号的汇总

汇编:根据汇编码以及特定的操作平台生成机器码

生成二进制可重定位的目标文件*.obj结尾:包含了text、data、bss、以及符号表、section table(保留了所有段的信息);

链接:第一步 合并所有段表,合并符号表,进行符号解析、为符号分配虚拟内存地址。

第二步:符号的重定向

运行的时候从main函数开始:创建虚拟地址空间到物理内存的映射,第一次映射的时候会发生缺页异常,需要分配物理页面,然后将其放入相应的页表。然后找到程序头program header,里面有两load,母的是为了表明把哪些段加载带代码段或者把哪些段加载到数据段,加载完成之后把可执行文件的入口地址写到CPU的pc寄存器里面,从磁盘映射到虚拟内存的映射方式是mmap,代码段数据段一一对应,虚拟内存上多一个文件头,然后再讲虚拟内存映射到页面上,映射方式是页目录页表映射。

二、系统调用的过程:

系统调用的过程依赖于系统调用号和系统调用表;

在调用系统函数read、open的时候产生软中断,0x80,16进制,也就是128,陷入内核,从用户空间却换到内核空间,换下去之前将系统调用号放入eax中,操作系统开始执行,从eax中取出系统调用号,再去系统调用表中找对应的系统调用。切换的开销大,避免反复切换。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值