当前位置:   article > 正文

小米C++开发 面试 准备阶段和部分真题_c++小米软件工程师面试题

c++小米软件工程师面试题

真题

C++

  • 函数指针和指针函数的区别  o(╥﹏╥)o
  • 堆和栈的区别
  • 函数重载?为什么返回值不可以区分函数重载?o(╥﹏╥)o
  • 封装、继承和多态的定义,自己描述一下这三者的区别和联系。
  • 多态的构成(应该是想听虚函数,当时没有想到  o(╥﹏╥)o)
    • 必须存在继承关系
    • 继承关系中必须存在同名的虚函数,并且子类和基类虚函数之间是遮蔽(覆盖)的关系
    • 存在基类指针,通过这个指针实现虚函数的调用
  • 进程相较于线程 还多包含一个socket通信o(╥﹏╥)o
  • STL

密码学

  • RSA padding模式是啥  大
  • RSA的位数 1024 2048 4096
  • 密码体制  密码的底层原理
  • 椭圆双曲线
  • 对称算法的模式  ofb  cfb
  • 服务器密码机如何对外提供服务?带宽

区块链

  • 数字钱包的标准
  • 检测账户余额的方法 UTXO

 准备阶段

C++

1,fork的底层实现是什么?

fork()函数的底层实现原理_everthing willl be ok-CSDN博客_fork原理

  • fork()系统调用通过复制一个现有进程来创建一个全新的进程。进程被存放在一个叫做任务队列的双向循环链表当中,链表当中的每一项都是类型为task_struct称为进程描述符的结构,也就是我们写过的进程PCB. 
  • Tips:内核通过一个位置的进程标识值或PID来标识每一个进程。//最大值默认为32768,short int短整型的最大值.,他就是系统中允许同时存在的进程最大的数目。
  • 可以到目录 /proc/sys/kernel中查看pid_max:

这里写图片描述

当进程调用fork后,当控制转移到内核中的fork代码后,内核会做4件事情:

  • 1、分配新的内存块和内核数据结构给子进程
  • 2、将父进程部分数据结构内容拷贝至子进程
  • 3、添加子进程到系统进程列表当中
  • 4、fork返回,开始调度器调度

 fork函数在底层到底做了什么呢?

  • Linux平台通过clone()系统调用实现fork()。 fork(),vfork()和clone()库函数都根据各自需要的参数标志去调用clone(),然后由clone()去调用do_fork(), 再然后do_fork()完成了创建中的大部分工作,该函数调用copy_process().做最后的那部分工作。

这里写图片描述

2,智能指针原理、使用和实现

理解智能指针

C++11中智能指针的原理、使用、实现 - wxquare - 博客园

  • 智能指针是利用了一种叫做RAII(资源获取即初始化)的技术对普通的指针进行封装,这使得智能指针实质是一个对象,行为表现的却像一个指针
  • 智能指针的作用是防止忘记调用delete释放内存和程序异常的进入catch块忘记释放内存。另外指针的释放时机也是非常有考究的,多次释放同一个指针会造成程序崩溃,这些都可以通过智能指针来解决

智能指针占据的字节数

  • shared_ptr  16字节
  • unique_ptr  8字节
  • weak_ptr  16字节

3,虚函数

C++虚函数详解_C语言中文网

C++ 虚函数表解析_陈皓专栏 【空谷幽兰,心如皓月】-CSDN博客_虚函数表

C++虚函数详解_Whitesad的博客-CSDN博客_c++虚函数

  • 只有派生类的虚函数遮蔽基类的虚函数(函数原型相同)才能构成多态(通过基类指针访问派生类函数)
  • 虚函数的声明的前提:首先看成员函数所在的类是否会作为基类。然后看成员函数在类的继承后有无可能被更改功能,如果希望更改其功能的,一般应该将它声明为虚函数。如果成员函数在类被继承后功能不需修改,或派生类用不到该函数,则不要把它声明为虚函数
  • 虚函数是运行时决议,与一般的编译时决议存在很大的不同
  • 虚函数指针:虚函数指针式一个指向函数的指针,指向的是子类内部实现的虚函数,当子类调用虚函数的时候,本质上是通过调用这个虚函数指针找到接口
  • 虚函数指针是确实存在的数据类型,在一个被实例化的对象中,它总是被存放在该对象的地址首位,这种做法的目的是为了保证运行的快速性。与对象的成员不同,虚函数指针对外部是完全不可见的,除非通过直接访问地址的做法或者在DEBUG模式中,否则它是不可见的也不能被外界调用

4,C++转型操作符  强制类型转换

  • const_cast 通常用来将对象的常量性转除(cast away the constness)。它是唯一有此能力的C++-style转型操作符
  • dynamic_cast用来执行继承体系中安全的向下转型或跨系转型动作。也就是说你可以利用它将指向基类对象的指针或者引用转型为指向派生类对象的指针或引用,并得知转型是否成功。如果转型失败,会以一个null指针(当转型对象是指针)或一个exception(当转型对象是引用)表现出来。dynamic_cast是唯一无法由旧式语法执行的转型动作,也是唯一可能消耗重大运行成本的转型动作
  • static_cast 基本上拥有与C旧式转型相同的威力与意义,以及相同的限制。例如将一个非 const 的对象转换为 const 对象,或将int 转换为 double等等。它也可以用来执行上述多种转换的反向转换,例如将void*指针转为typed指针,将pointer-to-base转为pointer-to-derived。但是他无法将const转为non-const,这个只有const-cast才能够办到
  • reinterpret_cast意图执行低级转型,实际动作及结果可能取决于编译器,这也就表示它不可移植。例如将一个pointer to int 转型为int。这一类转型在低级代码以外很少见

5,new和malloc的区别

c++ new 与malloc有什么区别 - ywliao - 博客园

  • new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是否能够是堆(问题等价于new是否能在堆上动态分配内存),这取决于operator new 的实现细节。自由存储区不仅可以是堆,还可以是静态存储区,这都看operator new在哪里为对象分配内存。
  • new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
  • new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL;malloc分配内存失败时返回NULL。
  • 使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算,而malloc则需要显式地指出所需内存的尺寸。

 5,C++仿照标准字符串类string,设计一个自己的字符串类String

C++仿照标准字符串类string,设计一个自己的字符串类String_之古的博客-CSDN博客_设计一个字符串类string

 主要的任务

  • 1)数据成员是字符指针,可自动适应不同的串长度。
  • 2)串复制,既可使用字符串常量进行复制,也可以使用其他的字符串对象进行复制。
  • 3)串赋值。既可赋值为字符串常量,也可以赋值为使用其他的字符串对象。
  • 4)串连接。重载“+”和“+=”。
  • 5)重载下标运算符[]。
  • 6)串比较。重载“==”和“<”。
  • 7)字符串输入和输出。重载“>>”和“<<”。
  1. #include<iostream>
  2. #include<cstring>
  3. using namespace std;
  4. class String {
  5. private:
  6. char * Pstr;
  7. public:
  8. String(const char* p = NULL) {
  9. if (p == NULL) {
  10. Pstr = new char[1];
  11. *Pstr = '\0';
  12. } else {
  13. Pstr = new char[strlen(p) + 1];
  14. strcpy(Pstr, p);
  15. }
  16. }
  17. String(const String& s): Pstr(new char[strlen(s.Pstr) + 1]) {
  18. strcpy(Pstr, s.Pstr);
  19. }
  20. ~String() {
  21. if (Pstr)
  22. delete[] Pstr;
  23. }
  24. String& operator=(const String& s) {
  25. if (Pstr == s.Pstr)
  26. return *this;
  27. else if (NULL == this) {
  28. delete Pstr;
  29. Pstr = new char[strlen(s.Pstr) + 1];
  30. strcpy(Pstr, s.Pstr);
  31. } else {
  32. strcpy(Pstr, s.Pstr);
  33. }
  34. return *this;
  35. }
  36. friend ostream & operator<<(ostream &out, const String &s) {
  37. out << s.Pstr;
  38. return out;
  39. }
  40. friend istream & operator >>(istream &in, String &s) {
  41. in >> s.Pstr;
  42. if(in)
  43. return in;
  44. }
  45. String operator+(const String &s2) {
  46. char *p=new char[strlen(Pstr) + strlen(s2.Pstr)+1];
  47. p=strcat(Pstr,s2.Pstr);
  48. return String(p);
  49. }
  50. void operator+=(const String &s2) {
  51. strcat(Pstr, s2.Pstr);
  52. }
  53. char& operator[](int n) {
  54. return Pstr[n];
  55. }
  56. int Length() {
  57. int n=strlen(Pstr) ;
  58. return n;
  59. }
  60. bool operator==(const String &s2) {
  61. return strcmp(Pstr, s2.Pstr) == 0;
  62. }
  63. bool operator<(const String &s2) {
  64. return strcmp(Pstr, s2.Pstr) < 0;
  65. }
  66. };

6,文件编译的过程

C 文件编译的过程_sxy19930313的博客-CSDN博客

gcc编译程序的四个阶段(预处理-编译-汇编-链接)_Aikenlan的博客-CSDN博客

预编译 :

  • 处理所有的注释 , 以空格代替
  • 将所有的 #define删除 ,并且展开所有的宏定义
  • 处理条件编译指令#if、#ifdef、#elf、#else、#endif
  • 处理#include,展开被包含的文件
  • 保留编译器需要使用的#pragma指令
  • 预处理指令:gcc    -E  file.c  -o  hello.i     

编译

  • 对预处理的文件进行一系列词法分析、语法分析和语义分析
    • 词法分析主要分析关键字,标识符,立即数等是否合法             
    • 语法分析主要分析表达式是否遵循语法规则                 
    • 语义分析是在语法分析的基础上进一步分析表达式是否合法        
  • 分析结束后进行代码优化生成相应的汇编代码文件            
  • 编译指令:  gcc   -S  file.c  -o hello.s

汇编

  • 汇编器将汇编代码转变为机器可以执行的指令  
  • 汇编阶段生成 二进制代码
  • 每个汇编语句几乎都对应一条机器指令 汇编指令:gcc  -c file.s  -o  hello.o             

链接

  •    链接器的主要作用是把各个模块之间相互引用的部分处理好,使得各个模块之间能够正确的衔接
  • 静态链接:在编译期间完成,链接文件是以目标文件为单位的,如果有多个可执行文件调用同一个函数,则该函数的目标文件就会生成多个副本,并与多个可执行文件一一对应。这样就造成来了内存空间得了浪费,同时函数库的更新也很麻烦。优点是程序在运行的时候已经包含了所有的程序,运行速度较快

  •  动态链接:在运行期间完成

 7,并行和并发的区别

面试必考的:并发和并行有什么区别? - 云+社区 - 腾讯云

  • 并发一段时间内  实现两个程序的执行  不可以同时
  • 并行是同一时刻  实现两个程序的执行  可以同时

 8,中断

为什么需要中断?_jwy2014的专栏-CSDN博客_为什么要使用中断

什么是中断,为什么要用中断?_old-li的blog-CSDN博客

9,逻辑地址 和 物理地址的区别

物理地址、虚拟地址和逻辑地址之间的区别_SoaringLee_fighting的技术专栏-CSDN博客

  • 物理地址就是,机器内主存的地址,包括RAM和ROM
  • 逻辑地址就是,程序运行在内存中,使用的地址。
  • 虚拟地址就是,cpu支持的内存空间远远大于机器主存的大小,这些多出来的空间对于程序来说是可以用的,这个时候的所有地址都称为虚拟地址

补充

C++ Primer:常量引用、引用常量、常量指针、指针常量 - 简书

  • 函数指针  指针函数
  • 常量指针  指针常量
  • 数组指针  指针数组
  • 常量引用  引用常量

函数指针 和  指针函数的区别

C++ 函数指针和指针函数-CJavaPy

指针函数

  • 指针函数本质是返回指针的函数,其本质是一个函数,返回的类型是指针。int *myfun(int x,int y);  在函数名前面多了一个*号,而这个函数就是一个指针函数。其返回值是一个 int 类型的指针。
  1. #include <iostream>
  2. using namespace std;
  3. #include<string.h>
  4. int *newAdd(int a, int b); // 声明指针函数
  5. int main() {
  6. int *p1 = NULL;
  7. p1 = newAdd(1, 2);
  8. printf("p1 = 0x%x \n", p1);
  9. printf("*p1 = %d \n", *p1);
  10. getchar();
  11. return 0;
  12. }
  13. int *newAdd(int a, int b) {
  14. int *p = (int *)malloc(sizeof(int));
  15. memset(p, 0, sizeof(int));
  16. printf("函数内:p = 0x%x \n", p);
  17. *p = a + b;
  18. printf("函数内:*p = %d \n", *p);
  19. return p;
  20. }
  • 在调用指针函数时,需要一个同类型的指针来接收其函数的返回值。 也可以将其返回值定义为 void*类型,调用时强制转换返回值为想要的类型。

函数指针

  • 函数指针,其本质是一个指针变量,该指针指向这个函数。函数指针就是指向函数的指针。声明格式如下:int (*myfun)(int x,int y);
  1. #include <iostream>
  2. using namespace std;
  3. int add(int x,int y){
  4. return x+y;
  5. }
  6. int sub(int x,int y){
  7. return x-y;
  8. }
  9. //函数指针
  10. int (*myfun)(int x,int y);
  11. int main(int argc, char *argv[])
  12. {
  13. //第一种写法
  14. myfun = add;
  15. cout << "(*myfun)(1,2) = " << (*myfun)(1,2) << endl;
  16. //第二种写法
  17. myfun = &sub;
  18. cout << "(*myfun)(5,3) = " << myfun(5,3) << endl;
  19. return 0;
  20. }

区别

1)定义不同

  • 指针函数本质是一个函数,其返回值为指针。
  • 函数指针本质是一个指针,其指向一个函数。

2)写法不同

  • 指针函数int* fun(int x,int y);
  • 函数指针int (*fun)(int x,int y);
  • 函数名带括号的就是函数指针,否则就是指针函数。

3)用法不同

  • 一个是函数,一个是变量

对特定地址进行赋值

如何给地址赋值?(转) - 张凌001 - 博客园

C语言中给指定的内存地址赋值(通过指针)_phenixyf的专栏-CSDN博客

  • 对于i分配的地址具有访问权限 
  1. int i = 0;
  2. std::cout << &i << std::endl;
  3. int *ptr = (int *)(&i);
  4. *ptr = 55;
  5. std::cout << *(int *)(&i)<<std::endl;

 全局变量和静态全局变量 区别

全局变量和静态全局变量_calm_peng-CSDN博客_静态全局变量

静态函数和普通函数的区别

  1. static函数与普通函数的区别:
  2.   用static修饰的函数,本限定在本源码文件中,不能被本源码文件以外的代码文件调用。而普通的函数,默认是extern的,也就是说,可以被其它代码文件调用该函数。
  3.   在函数的返回类型前加上关键字static,函数就被定义成为静态函数。普通 函数的定义和声明默认情况下是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。因此定义静态函数有以下好处:
  4.   <1> 其他文件中可以定义相同名字的函数,不会发生冲突。
  5.   <2> 静态函数不能被其他文件所用。

计算机网络

OSI七层网络模型,以及每层对于的网络协议

OSI七层模型及对应的网络协议_小麻花-CSDN博客_七层模型对应的协议

这里写图片描述

https的底层实现,原理是什么?

HTTPS底层实现原理_风某人~Wind的博客-CSDN博客_https的底层原理

  • 证书(CRT文件,里面包含了公钥,CA的签名,过期时间,申请人提交的信息)
  • https = http+SSL 12次

TCP  UDP 区别

一文搞懂TCP与UDP的区别 - Fundebug - 博客园

UDPTCP
是否连接无连接面向连接
是否可靠不可靠传输,不使用流量控制和拥塞控制可靠传输,使用流量控制和拥塞控制
连接对象个数支持一对一,一对多,多对一和多对多交互通信只能是一对一通信
传输方式面向报文面向字节流
首部开销首部开销小,仅8字节首部最小20字节,最大60字节
适用场景适用于实时应用(IP电话、视频会议、直播等)适用于要求可靠传输的应用,例如文件传输


 

Linux

Linux 驱动模型 

Linux驱动学习:应先了解驱动模型 - 知乎

[Linux]互斥机制(中断屏蔽、原子操作、自旋锁、信号量)

[Linux]互斥机制(中断屏蔽、原子操作、自旋锁、信号量)_Younix凌乱的草稿本-CSDN博客

Linux进程之间通信方式

进程间8种通信方式详解 - 云+社区 - 腾讯云

进程间通信的方式——信号、管道、消息队列、共享内存 - 0giant - 博客园

用户态和内核态的区分

用户态和内核态的区别 - Gizing - 博客园

查看linux的位数 不能使用sizeof

查看当前linux系统位数 - controlV - 博客园

  • uname -a
  • uname -r
  1. 命令1:getconf LONG_BIT
  2. 结果:64
  3. 命令2:uname -a
  4. 结果:Linux Test004MUJUP 2.6.32-431.23.3.el6.x86_64 #1 SMP Wed Jul 16 06:12:23 EDT 2014 x86_64 x86_64 x86_64 GNU/Linux
  5. 命令3:uname -r
  6. 结果:2.6.32-431.23.3.el6.x86_64
  7. 命令4:cat /proc/version
  8. 结果:Linux version 2.6.32-431.23.3.el6.x86_64 (mockbuild@x86-027.build.eng.bos.redhat.com) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC) ) #1 SMP Wed Jul 16 06:12:23 EDT 2014

参考资料

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/919556
推荐阅读
相关标签
  

闽ICP备14008679号