校招八股:C/C++开发工程师常见笔试、面试题目不完全汇总【很基础】
这里汇总一些C/C++开发岗的常见面试八股题,都属于比较基础、偏理论性的题目。换句话说,如果这些题目答不上来,可能会给面试官留下的基础不好的印象,尤其是科班生哈。
废话不多说,直接开始。
一、C/C++篇
1. 基础中的基础篇
简述C和C++的区别
难度:⭐ 被考频率:⭐⭐⭐
如果你面试的是C++开发岗,有些面试官会把这个问题作为第一个问题。C和C++的区别细究起来的话太多了,因此,我们只要回答它们最大的,也是最主要的区别就好了。如果面试官想知道其余细节的话会继续追问的。
C和C++的区别如下:
思想上:C是面向过程的,它的主要特点是函数。编程思想是将问题分解成不同的步骤,并调用函数来依次实现这些步骤。
C++是面向对象的,它的主要特点是类和对象。编程思想是将数据和数据操作封装成不同的类,通过创建这些类的对象并调用对象的成员函数来实现对数据模型的操作。应用上:C的应用更偏底层,常常用于嵌入式开发、驱动开发等直接与硬件交互的领域。
C++由于它优秀的面向对象机制,在大型应用程序的开发方面也表现出色。C++是对C的扩充。除了添加了面向对象机制以外,C++还添加的机制常用到的有:类模板、异常处理、运算符重载、标准模板库(STL)、命名空间(namespace)、流(stream)等等。
C程序从源程序到二进制机器代码的过程和gcc指令
难度:⭐⭐ 被考频率:⭐⭐⭐
举例:gcc编译源代码hello.c的过程。
1. 预处理(Preprocessing):gcc -E hello.c -o hello.i
处理所有预编译指令,即源代码文件中以“#”开头的指令,具体为:
a. 展开宏定义#define。
b. 处理条件编译指令#ifdef。
c. 处理文件包含指令#include。
d. 删除注释"//“和”/* */"。
e. 添加行号和文件标识,便于编译器在编译阶段产生错误和警告提示时能够显示行号。
2. 编译(Compilation):gcc -S hello.i -o hello.s
对预处理后的文件进行词法分析、语法分析、语义分析、中间代码的生成和优化,最后得到汇编代码文件。
a. 词法分析(Lexical analysis):词法分析器会从左到右逐个字符读入源程序,按照词法规则将源代码分割成一个个单词(Token),检查词法错误,并输出二元组<单词类别,单词属性>方便后续编译过程的引用。
b. 语法分析(Syntax analysis):识别由词法分析器输出的单词符号序列,构造一棵语法树。语法树指出了词法单元流的语法结构,可以判断是否符合语法规范。常见的语法分析方法分为自下而上和自上而下两大方法。
c. 语义分析(Semantic analysis):使用语法树和单词符号表中的信息,进一步检查源程序是否满足语言定义的语义约束。这一步分析的时静态语义,也就是在编译期间能分析的语义,而动态语义指在运行期间才能确定的语义。
d. 中间代码生成:根据语义分析的输出,生成类机器语言的中间代码,如三地址代码。
e. 中间代码优化:改进中间代码的质量。
f. 目标代码生成:将中间代码映射成目标语言(汇编语言)。
3. 汇编(Assembly):gcc -c hello.s -o hello.o
汇编器将汇编代码翻译成可执行的机器码。每一条汇编语句对应一条机器指令,因此汇编器的工作就是根据汇编指令和机器指令的对照表一一翻译过来即可。
4. 链接(Linking):gcc hello.o -o hello
链接器将多个汇编后得到的机器码文件链接,从而生成一个可执行程序。分为静态链接和动态链接两种方式。
a. 静态链接:在链接可执行文件时,链接器会找到所有被用到的源代码,将它们复制并组合起来形成一个可执行文件。优点是由于可执行文件中已经具备了执行程序需要的全部内容,因此执行时运行速度较快。缺点是如果有多个可执行程序需要引用同一个目标文件,每个可执行程序都会复制一份该文件的副本,造成空间浪费;另外,如果某个目标文件被修改了,则所有引用该文件的可执行程序都需要重新编译。
b. 动态链接:程序被拆分成独立的部分存储,只有运行时才链接在一起形成完整的程序。优点是即使多个程序依赖同一个动态链接库,也不需要将这个库复制多份,而是所有程序共享这个库;此外在程序更新时只更新被修改的库,不需要重新链接所有程序。缺点是因为将链接过程推迟到程序运行时期,所以会对程序性能产生损失。
C程序的内存管理
难度:⭐⭐ 被考到频率:⭐⭐⭐⭐
C或C++语言编写的程序在处理机上运行,通常被分成五段:
栈区:存放函数中声明的局部变量、函数的形参和返回值。地址空间“向下减少”。
堆区:保存动态分配的内存区域,可由程序员向操作系统申请和自行释放。
静态区(全局区):存储全局变量和静态变量。静态区内存程序开始时创建,直到程序运行结束后才会被释放。
常量区:保存常量,在程序运行期间不能被修改的量,如字符串常量"abcd"。
代码区:存放程序代码,二进制机器指令形式,只读。
补充: 一个由C语言程序编译得到的可执行二进制文件,从硬盘上被加载到内存空间上运行的过程,内存的分配和管理机制。
一个C语言编译的可执行二进制文件,通常被分为三段:代码段(text)、数据段(data)、堆栈段(BSS)。
装载到内存上被分为五段:栈区(stack)、堆区(heap)、未初始化变量(BSS)、初始化变量(data)、代码段(text)。
可执行文件中,text段存放二进制程序;data段存放静态初始化的数据,即赋初值的全局变量和static变量;BSS段存放未初始化的数据,即没有赋初值的全局变量和static变量,在可执行文件中BSS段并不占用实际空间,而是只记载该区大小的数值,程序载入时菜实际分配空间,且该控件由系统初始化为零。
应用程序加载到内存空间时,操作系统根据可执行文件中header中的内容,为text段、data段、为BSS段分配相应的内存空间,将文件中
data段和text段内容拷贝到内存中,将BSS段初始化为零,同时为堆区和栈区分配空间并维护。
堆和栈的区别
难度:⭐⭐ 被考到频率:⭐⭐⭐⭐⭐
堆 | 栈 |
---|---|
用户自己申请 | 系统分配 |
由不连续的内存块构成的链表,大小可以调整 | 连续空间,大小固定 |
速度慢,会产生碎片 | 速度快,不会产生碎片 |
向高地址增长 | 向低地址增长 |
由库函数(malloc/realloc/free、new/delete)提供服务 | 由系统提供服务 |
什么叫内存泄漏
难度:⭐⭐ 被考到频率:⭐⭐
内存泄漏,通常指堆内存泄漏,程序员向系统申请任意大小的堆内存块,使用完毕必须进行显式的内存释放,否则该内存不能再次被使用。即使用malloc、realloc、new申请的空间,必须使用free、delete进行释放。
什么叫作用域
2. C++特性篇
C++的三大特征
难度:⭐ 被考到频率:⭐⭐
继承、多态、封装。
继承:一个对象可以继承另一类对象的特征和能力。目的是避免公用代码的重复开发,减少代码和数据冗余。
多态:为不同数据类型的实体提供统一的接口,程序运行时,相同的消息可能会送给多个不同的类别之对象,而系统可依据对象所属类别,引发对应类别的方法,而有不同的行为
封装:把客观的事物抽象成一个类,就是将数据和方法打包在一起,加以权限的区分,达到保护并安全使用数据的目的。
静态多态和动态多态
难度:⭐⭐ 被考到频率:⭐⭐
多态分为静态多态和动态多态。
静态多态:在编译期间决定程序的执行过程。包括函数重载和泛型编程,泛型编程包括函数模板和类模板。
动态多态:在程序运行时根据被引用对象的实际类型判断调用哪个方法。包括虚函数。
C++中重载、重写(覆盖)和隐藏的区别
难度:⭐⭐ 被考到频率:⭐⭐
- 重载(overload):同一作用域存在的名称相同、参数类型或参数数目不同的函数。在函数调用时根据不同的参数来决定具体调用哪个函数。
class A {...void fun(int);void fun(double, double);...
}
- 重写(覆盖)(override)
派生类中的函数覆盖基类中的同名函数,要求两个函数具有相同的参数个数、参数类型和返回值类型,且基类中的函数必须是虚函数。重写指的是重写基类函数中的函数体。
class A { //父类
public:virtual int fun(int a) {...}
}class B : public A { //子类
public:virtual int fun(int a) override {...} //重写,加override可以确保是重写父类的函数
}
- 隐藏(hide)
在某些情况下,派生类中的函数屏蔽了基类中的同名函数的现象。如果想调用基类的函数必须加作用域限定符。具体情况有:
(1)派生类和基类中具有同名函数,两个函数参数列表相同,且基类的函数没有被声明称虚函数。
class A { //父类
public:void fun(int a) {...}
};class B : public A { //子类
public:void fun(int a) {...} //隐藏父类的fun函数
};int main() {B b;b.fun(2); //调用B中的fun函数b.A::fun(2); //调用A中fun函数return 0;
}
(2)派生类和基类中具有同名函数,两个函数参数列表不同,无论基类的函数是不是虚函数都会被隐藏。
class A { //父类
public:void fun(int a) {...}
};class B : public A { //子类
public:void fun(char* a) {...} //隐藏父类的fun函数
};int main() {B b;b.fun(2); //报错,调用B中的fun函数,但参数类型出错b.A::fun(2); //调用A中fun函数return 0;
}
虚函数
难度:⭐⭐ 被考到频率:⭐⭐⭐
基类和派生类中可以出现名字相同、参数个数和参数类型都相同的函数,直接调用时编译器会让派生类函数覆盖基类函数,或者通过添加作用域限定符来调用基类函数。因此,
下面的例子中,基类和派生类中具有同名函数display()。
#include <iostream>
using namespace std;//定义基类
class Point {protected:float x, y;
public:Point(float x = 0, float y = 0); //构造函数void display();
};Point::Point(float a, float b) : x(a), y(b) {}
void Point::display() {cout << "[x, y] = [" << x << ", " << y << "]" << endl;
}//定义派生类,继承自父类Point
class Circle : public Point {private:float radius;
public:Circle(float x = 0, float y = 0, float r = 0); //构造函数void display(); //与基类中同名的函数
};Circle::Circle(float a, float b, float r) : Point(a, b), radius(r) {}
void Circle::display() {cout << "[x, y] = [" << x << ", " << y << "]; r = " << radius << endl;return;
}int main() {Point p(1, 1); //定义一个基类对象Circle c(5, 5, 2.5); //定义一个派生类对象Point* pt; //定义一个基类指针pt = &p; //指针指向基类对象pt->display(); //调用同名函数pt = &c; //指针指向派生类对象pt->display(); //调用同名函数return 0;
}
输出结果。
[x, y] = [1, 1]
[x, y] = [5, 5]
分析输出结果可以发现,第一行调用的基类的成员函数,第二行虽然指针指向了派生类,但由于指针的类型时基类的,调用的依旧是基类的成员函数。如果我们希望指针指向什么类的对象,就调用该类的成员函数,则需要将基类的display函数声明为虚函数。
修改代码,其他地方不修改,只是在基类Point中的声明display函数时添加关键字virtual,如下。
//定义基类
class Point {protected:float x, y;
public:Point(float x = 0, float y = 0); //构造函数virtual void display();
};
... ...
运行结果如下。
[x, y] = [1, 1]
[x, y] = [5, 5]; r = 2.5
在设定虚函数之前,基类指针本应该指向基类对象,如果指向派生类对象,则必须进行指针类型转换,将派生类的指针转换成基类指针,因此通过该指针也只能调用派生类对象中的基类部分。
设定虚函数之后,派生类的基类部分取代了原本基类中的虚函数,因此即使基类指针指向派生类对象,也会调用派生类中的成员函数。
虚函数的原理
难度:⭐⭐⭐ 被考到频率:⭐⭐
实现原理:虚函数表+虚表指针
虚函数表(vtbl):是一个数组,每个元素都用来存储虚函数的地址。每个类有一个自己的虚函数表,由于类中虚函数的个数可以在编译时期就能确定,因此虚函数表的大小在编译时期就可以确定。虚函数表是全局共享的,存储在全局数据区,在编译时完成构造,全局可用。
虚表指针(vptr):编译器为每个类对象添加一个隐藏成员,隐藏成员保存了一个指向虚函数表的指针,
未完待续……
校招八股:C/C++开发工程师常见笔试、面试题目不完全汇总【很基础】相关推荐
- 嵌入式笔试面试题目系列(汇总)
嵌入式笔试 一.进程与线程 1.什么是进程.线程,有什么区别? 2.多进程.多线程的优缺点 3.什么时候用进程,什么时候用线程 4.多进程.多线程同步(通讯)的方法 5.进程线程的状态转换图 6.父进 ...
- 运维开发工程师常见的面试
文章目录 linux kubernetes mysql redis ansible prometheus linux linux常见的排障命令说几个 vmstat能做什么? 查看网卡流量怎么做? 请你 ...
- 测试开发工程师实习—第一次面试总结
面试总结 前言 一面 二面 前言 大家好!我是一个自学测试的本科生Test_晓 这篇Blog整理了我第一次面试测试开发实习生岗位的面试题目以及整理搜集的相关知识点和资料 其中有些问题的解答还需要完善, ...
- 面试官吐槽:“软件测试员就是不行!”网友:我能把你面哭了!——软件测试笔试面试题目完全汇总
软件测试笔试面试题目完全汇总 软件缺陷: 1)软件未实现产品说明书要求的功能 2)软件出现了产品说明书指明不应该出现的错误 3)软件实现了产品说明书未提到的功能 4)软件未实现产品说明书虽未明确提及但 ...
- 嵌入式工程师的经典面试题目及答案
上个星期,去深圳一家搞ARM开发的公司面试,HR叫我做了一份卷子,里面都是C编程,心中暗喜,因为这些题基本上都在程序员面试宝典里见过.后来回到学校,在网上搜索,原来这些题都是嵌入式工程师的经典面试题目 ...
- 12种排序算法:原理、图解、动画视频演示、代码以及笔试面试题目中的应用
0.前言 从这一部分开始直接切入我们计算机互联网笔试面试中的重头戏算法了,初始的想法是找一条主线,比如数据结构或者解题思路方法,将博主见过做过整理过的算法题逐个分析一遍(博主当年自己学算法就是用这种比 ...
- 2021秋招嵌入式笔试面试题目汇总
本系列按类别对题目进行分类整理,这样有利于大家对嵌入式的笔试面试考察框架有一个完整的理解. 欢迎关注公众号<嵌入式Linux系统开发>,定期分享硬件.单片机.嵌入式Linux技术文章,支 ...
- 46家中外知名企业笔试面试题目
http://blog.sina.com.cn/s/blog_4897add3010009wj.html 微软 智力题 1.烧一根不均匀的绳子,从头烧到尾总共需要1个小时,问如何用烧绳子的方法来确 定 ...
- 【数字设计】经纬恒润_2023届_笔试面试题目分享
芯片设计验证社区·芯片爱好者聚集地·硬件相关讨论社区·数字verifier星球 四社区联合力荐!近500篇数字IC精品文章收录! [数字IC精品文章收录]学习路线·基础知识·总线·脚本语言·芯片求职· ...
最新文章
- JSP 日期处理概述
- mysql 中文搜索插件_支持中文的MySQL 5.1+ 全文检索分词插件
- JS中的Math.ceil和Math.floor函数的用法
- linux端口 fcs校验,我如何接收错误的以太网帧并禁用CRC / FCS计算?
- C++11的for循环使用auto的新用法
- yiilite.php,缓存 - yii在哪些情况下可以加载yiilite.php?
- nodejs 复制、移动文件
- linux脚本生成数字写入文本,4.2 编写Shell脚本(P80-85)——《Linux就该这么学》学习笔记16...
- iOS模拟器中修改textField键盘类型为中文输入和键盘弹出方法
- Tensorflow Python3 做神经网络(视频教程)
- 【突发】解决remote: Support for password authentication was removed on August 13, 2021. Please use a perso
- Python中文转拼音代码(支持全拼和首字母缩写)
- 大型网络整体安装与配置解决方案
- 推荐WordPress 必备的常用插件及插件功能介绍
- Github网站中文汉化浏览器插件
- java开学考试感想及代码
- VBA生成KML文件
- 计算机计划实施800字,大学计划书范文800字3篇
- 专用神经网络处理器的芯片,cpu可以跑神经网络吗
- 黄河科技学院计算机等级考试成绩查询,全国计算机等级考试成绩查询汇总