C++高频面试题

时间:2022-09-05 22:59:49
  1. malloc/free和new/delete的区别

    malloc/free是C语言的标准库函数,new/delete是C++的运算符。 
    由于malloc/free是库函数而不是运算符,不在编译器控制权限之内。
    对于用户自定义的对象而言,用maloc/free无法满足动态管理对象的要求。(废话) 
    new是类型安全的,malloc不是。int a=new float[2]是通不过编译的,new 内置了sizeof、类型转换和类型安全检查功能。而malloc的返回类型是void*,malloc只负责申请空间。
     对于非内部数据类型的对象而言,new 在创建动态对象的同时完成了初始化工作,malloc则没有,calloc初始化,内存为0。对于内部数据类型需要加圆括号new才执行初始化工作。string属于非内部数据类型。int arr=new int[10];没有初始化,int arr=new int[10]();初始化为0。
    new由两步构成:1.operator new    2.调用构造函数      第一步相当于malloc的功能,但是operator new可以重载,可以自定义内存分配策略,甚至不做内存分配,甚至分配到非内存设备上,malloc则无能为力。
  2. 多进程编程和多线程编程的区别,各自有什么好处

     fork之后,理论上(不考虑copy-on-write技术)子进程会复制父进程的数据空间、栈、堆,而创建线程则不会发生复制,变量在线程之间都是共享的。(由于fork之后通常会很快调用exec,如果一fork子进程就立刻复制父进程的数据段的话,刚刚复制完exec又要重写进程的代码段和数据段,则刚才的复制没有实际的意义,所以就有了copy-on-write技术)。
     对于同一进程的不同线程来说,每个线程的局部变量都是私有的,而全局变量、局部静态变量、分配于堆的变量都是共享的。在对这些共享变量进行访问时,如果要保证线程安全,则必须通过加锁的方式。
  3. 程序从源文件到执行要经历哪些过程
     
    2个步骤:编译和链接。
    编译:从源文件(.c)转换成机器可识别的目标二进制文件(.o)。
    链接:将若干个目标文件链接起来,生成完整的可执行文件,实际上是把它们的各种符号引用和符号定义转换为可执行文件中的合适信息(一般是虚拟内存地址)的过程。 
    在正式编译前有个预处理的过程:头文件的包含、宏定义的扩展、条件编译的选择等。
    编译是通过多遍扫描源文件来完成的:第一遍扫描做词法分析;第二遍扫描做语法分析;第三遍扫描做代码优化和存储分配;第四遍扫描做代码生成。
    有的编译器会先生成汇编语言,再生成目标代码。
    链接的过程可分不静态链接和动态链接两种。 静态链接实际上是gcc在后台调用了ld来完成的,它把被链接的程序加载到一个绝对的虚拟内存地址。动态链接则是程序运行期间系统调用动态链接器(ld-linux.so)自动链接的过程,比如对于puts,因为它是动态连接库libc.so中定义的函数,所以会在程序运行时通过动态符号链接找出puts函数在内存中的地址,以便程序调用该函数。
  4. 线程安全和可重入的关系

    可重入的函数一定是线程安全的,但反过来不一定成立。 
    线程安全:一个函数被称为线程安全的,当且仅当被多个并发线程反复调用时,它会一直产生正确的结果。 可重入:所谓“重入”,常见的情况是,程序执行到某个函数foo()时,收到信号,于是暂停目前正在执行的函数,转到信号处理函数,而这个信号处理函数的执行过程中,又恰恰也会进入到刚刚执行的函数foo(),这样便发生了所谓的重入。此时如果foo()能够正确的运行,而且处理完成后,之前暂停的foo()也能够正确运行,则说明它是可重入的。 
    要确保函数可重入,需满足一下几个条件:
    1、不在函数内部使用静态或全局数据 
    2、不返回静态或全局数据,所有数据都由函数的调用者提供。 
    3、使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。 
    4、不调用不可重入函数。
  5. 编程实现一个hash table

    已有总结 
  6. 用MapReduce实现一个矩阵和它的转置相乘

    A×B=C
    原矩阵的第i行与第j行的向量积,构成结果矩阵第i行第j列的元素。
    比如原矩阵为:
    5   7   9
    1   3   6 
    在Mapper阶段输出:
    <(0,-1),(5   7   9)>、<(0,0),(5   7   9)>、<(1,0),(5   7   9)>
    <(1,-1),(1   3   6)>、<(0,1),(1   3   6)>、<(1,1),(1   3   6)>
    key.first相同的到同一个Reducer。key.first表示在矩阵C中的行号,key.second表示在矩阵C中的列号。key.second=-1表示矩阵A中的该行要和矩阵B中的每一行相乘。
    在Reducer中收到:
    <(0,-1),(5   7   9)>、<(0,0),(5   7   9)>、<(0,1),(1   3   6)>
    <(1,-1),(1   3   6)>、<(1,0),(5   7   9)>、<(1,1),(1   3   6)>
    乘积:  
    155.0   80.0
    80.0     46.0