C++ 复习笔记(面试问题)一
问: C++与C 语言的区别
- c++ 支持带默认值的参数 参数可以从右到左给 , 可以在声明的时候给,也可以在定义的时候给 在函数调用的时候传入的参数为立即数 和有默认参数的不传参调用效率相同 ,而传入变量增加 mov 指令 如sum(10,10) 和sum(a+b);
- c++ 支持函数重载 ,c语言编译器产生函数符号时:根据函数名生成 而c++会根据函数名和参数列表一起生成 ,所以函数名相同,参数列表不同(参数个数,参数类型),可以构成函数重载。(重载函数在编译阶段确定,call 指令需要函数的逻辑地址)
- 动态申请空间的区别(malloc/free new/delete)
- malloc 是c的库函数 new是运算符
- malloc 是按字节开辟空间,返回值需要类型强转,而new是按类型开辟
- malloc 只负责申请内存,不初始化,new可以申请和初始化一起进行
- malloc 申请内存失败返回nullptr 而 new失败抛出异常
- 底层new是通过malloc 开辟的 但是优化于malloc
- delete也是运算符,而free是c库函数
- delete释放内存是先调用析构函数,在释放 ,而 free直接释放
- c++的引用
- 引用&在底层是个常量指针(const *p)因此底层和指针一样,所占空间和指针相同
- 在功能方面可以满足指针的多数需求
- 比指针更为安全 指针可以为nullptr 而引用不能为空,必须要初始化对象,可以避免也指针,引用不进行地址操作,使得访问地址更安全
- 有const 指针没有const 引用
- 指针可以有多级指针,而饮用只有一级
- ++运算符意义不一样 指针是跳到连续地址的下一个对象 引用是对对象的操作
- 指针有自己的一块空间,引用没有
- 动态开辟空间返回要用指针,引用会引起内存泄露
- struct 和class struct默认类型为public 而class 为private
问:c++ 和c 是本地编译语言,请问编译连接原理是什么?
答:源文件经过 预编译-----形成(.i)文件-----编译(.s)文件------汇编(linux:.o)文件/(window:.obj) 文件-----链接(可执行文件)
- 预编译过程: 处理以#号开头的命令 展开头文件 进行宏替换 删除注释(注意#pragma lib 链接库在链接阶段 #pragma link 指定入口函数在链接阶段)
- 编译阶段: 语法语义编译,词法解释 代码优化
- 汇编阶段: 由汇编指令生成二进制指令 符号表的输出
- 链接阶段: a: 所有.o文件字段合并,符号表合并,进行符号解析 b: 链接的核心:符号的从、重定位(a 完成后 符号分配了虚拟地址空间)
问: 全局变量和未初始化的全局变量有什么区别?
答:程序运行的时候,数据需要从磁盘加载到物理内存中,Linux会为每个进程分配4G的虚拟地址空间,全局变量初始化过的会放在.data段,而未初始化的或者初始化为0的放在.bss段,.bss段不占用实际磁盘空间,程序执行前,全部清零。
问: 堆和栈的区别?
答:(从地址空间分配开始回答):在x86体系下32位Linux系统中对于一个进程会有.text段,.rodata段, .data段, .bss段,.heap堆区 .stack栈区 ,栈区是系统分配的,由系统管理,以函数为单位,函数栈帧,局部变量,形参变量都放在栈的内存上,堆内存是程序员动态开辟 c语言用malloc/free c++用new/delete 需要自己管理,因此很容易造成内存泄露,而栈不会,栈的内存分配效率高,内存是连续的,一般为2M或者10M (Linux上用ulimit -s查看 可修改),堆内存分配相对应栈要效率低一点,容易产生碎片,不一定连续。栈是由高地址向低地址开辟,堆是由低地址到高地址
拓展:
①管理方式:栈由编译器自动管理;堆由程序员控制,使用方便,但易产生内存泄露。
②生长方向:栈向低地址扩展(即”向下生长”),是连续的内存区域;堆向高地址扩展(即”向上生长”),是不连续的内存区域。这是由于系统用链表来存储空闲内存地址,自然不连续,而链表从低地址向高地址遍历。
③空间大小:栈顶地址和栈的最大容量由系统预先规定(通常默认2M或10M);堆的大小则受限于计算机系统中有效的虚拟内存,32位Linux系统中堆内存可达2.9G空间。
④存储内容:栈在函数调用时,首先压入主调函数中下条指令(函数调用语句的下条可执行语句)的地址,然后是函数实参,然后是被调函数的局部变量。本次调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的指令地址,程序由该点继续运行下条可执行语句。堆通常在头部用一个字节存放其大小,堆用于存储生存期与函数调用无关的数据,具体内容由程序员安排。
⑤分配方式:栈可静态分配或动态分配。静态分配由编译器完成,如局部变量的分配。动态分配由alloca函数在栈上申请空间,用完后自动释放。堆只能动态分配且手工释放。
⑥分配效率:栈由计算机底层提供支持:分配专门的寄存器存放栈地址,压栈出栈由专门的指令执行,因此效率较高。堆由函数库提供,机制复杂,效率比栈低得多。Windows系统中VirtualAlloc可直接在进程地址空间中分配一块内存,快速且灵活。
⑦分配后系统响应:只要栈剩余空间大于所申请空间,系统将为程序提供内存,否则报告异常提示栈溢出。
操作系统为堆维护一个记录空闲内存地址的链表。当系统收到程序的内存分配申请时,会遍历该链表寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点空间分配给程序。若无足够大小的空间(可能由于内存碎片太多),有可能调用系统功能去增加程序数据段的内存空间,以便有机会分到足够大小的内存,然后进行返回。,大多数系统会在该内存空间首地址处记录本次分配的内存大小,供后续的释放函数(如free/delete)正确释放本内存空间。
此外,由于找到的堆结点大小不一定正好等于申请的大小,系统会自动将多余的部分重新放入空闲链表中。
⑧碎片问题:栈不会存在碎片问题,因为栈是先进后出的队列,内存块弹出栈之前,在其上面的后进的栈内容已弹出。而频繁申请释放操作会造成堆内存空间的不连续,从而造成大量碎片,使程序效率降低。
可见,堆容易造成内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和内核态切换,内存申请的代价更为昂贵。所以栈在程序中应用最广泛,函数调用也利用栈来完成,调用过程中的参数、返回地址、栈基指针和局部变量等都采用栈的方式存放。所以,建议尽量使用栈,仅在分配大量或大块内存空间时使用堆。
使用栈和堆时应避免越界发生,否则可能程序崩溃或破坏程序堆、栈结构,产生意想不到的后果
问:c++中宏和内联函数的区别?
答:
- 宏替换发生在预编译阶段,是纯粹的字符串替换,而内联函数发生在编译阶段,有完整的语句检查,比宏安全
- 宏替换不会出现压栈出栈的开销,提高了效率,而inline函数本质上就是函数,在代码上比较简短,不能包含复杂的控制语句 switch,while....不能自身调用自身,否则会被编译器变成普通函数,因为内联函数在调用点直接展开,不会有函数调用开销,
- 内联函数不生成相应的函数符号
问:内联函数和普通函数的区别
答
- 内联函数和普通函数传参机制相同,但是编译器会在内联函数的调用点展开内联函数,因此没有函数调用的开销
- 普通函数调用,是在函数入口去执行函数体,函数本身只有一份,执行完会继续执行函数入口的下一行,而内联函数有N次调用,展开N次
- 内联函数有一定的限制,不能有复杂的控制语句,如果有,编译器也会将内联函数变成普通函数

浙公网安备 33010602011771号