C++面试高频(C++基础)
C++基础
1 简述C++从代码到可执行二进制文件的过程⭐⭐⭐⭐⭐
C++和C语言类似,一个C++程序从源码到执行文件,有四个过程,预编译、编译、汇编、链接。
预编译:这个过程主要的处理操作如下:
(1) 将所有的#define删除,并且展开所有的宏定义
(2) 处理所有的条件预编译指令,如#if、#ifdef
(3) 处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。
(4) 过滤所有的注释
(5) 添加行号和文件名标识。
编译:这个过程主要的处理操作如下:
(1) 词法分析:将源代码的字符序列分割成一系列的记号。
(2) 语法分析:对记号进行语法分析,产生语法树。
(3) 语义分析:判断表达式是否有意义。
(4) 代码优化:
(5) 目标代码生成:生成汇编代码。
(6) 目标代码优化:
汇编:这个过程主要是将汇编代码转变成机器可以执行的指令。
链接:将不同的源文件产生的目标文件进行链接,从而形成一个可以执行的程序。
2 知道动态链接与静态链接吗?两者有什么区别⭐⭐⭐⭐
链接分为静态链接和动态链接。
1 静态链接,是在链接的时候就已经把要调用的函数或者过程链接到了生成的可执行文件中,就算你在去把静态库删除也不会影响可执行程序的执行;生成的静态链接库,Windows下以.lib为后缀,Linux下以.a为后缀。
2 而动态链接,是在链接的时候没有把调用的函数代码链接进去,而是在执行的过程中,再去找要链接的函数,生成的可执行文件中没有函数代码,只包含函数的重定位信息,所以当你删除动态库时,可执行程序就不能运行。生成的动态链接库,Windows下以.dll为后缀,Linux下以.so为后缀。
区别
1 静态链接是将各个模块的obj和库链接成一个完整的可执行程序;而动态链接是程序在运行的时候寻找动态库的函数符号(重定位)
2 静态链接运行快、可独立运行;动态链接运行较慢(事实上,动态库被广泛使用,这个缺点可以忽略)、不可独立运行。
3 静态链接浪费空间,存在多个副本,同一个函数的多次调用会被多次链接进可执行程序,当库和模块修改时,main也需要重编译;动态链接节省空间,相同的函数只有一份,当库和模块修改时,main不需要重编译。
3 C++编译时和C有什么不同,在c++中怎么用c?⭐⭐⭐
C++与C在编译时的主要区别有以下几点:
- 由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。
- 语法和功能:C++相比C具有更多的语法和功能。C++引入了面向对象编程的概念,包括类、继承、多态等。此外,C++还提供了更多的库和工具,如标准模板库(STL)和异常处理机制等。
- 兼容性:C++是C的超集,这意味着C的源代码可以直接在C++中编译和运行。C++编译器会自动识别和处理C的语法,因此可以使用C代码编写的功能和库。
在C++中使用C代码有多种方式,其中常见的几种方式包括:
使用extern "C"
进行函数声明:在C++中,使用extern "C"
修饰C代码的函数声明,以告诉编译器使用C的名称重载规则。
extern "C" { // C函数声明 int add(int a, int b); }
在C++中包含C的头文件:在C++源文件中直接包含C的头文件,即#include "my_c_code.h"
,然后直接使用其中声明的C函数和数据结构。
#include "my_c_code.h" int main() { int result = add(3, 4); // 调用C函数 return 0; }
使用#ifdef __cplusplus
进行条件编译:在C的头文件中使用条件编译,根据__cplusplus
宏定义来区分C和C++环境,在C++环境下使用extern "C"
修饰C函数声明。
#ifdef __cplusplus extern "C" { #endif // C函数声明 int add(int a, int b); #ifdef __cplusplus } #endif
4 请你说说什么是宏?⭐⭐⭐⭐⭐
#define命令是一个宏命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。
该命令有两种格式:一种是不带参数的宏定义,另一种是带参数的宏定义。
5 为什么要少使用宏?C++有什么解决方案?⭐⭐⭐⭐⭐
在C++中,推荐尽量避免过多使用宏的原因有以下几点:
- 可读性差:宏通常使用简单的文本替换机制,在代码中展开为复杂的表达式或语句,导致代码可读性降低。
- 潜在的副作用:宏的使用可能导致潜在的副作用,比如多次求值、修改变量等,这可能导致意外行为和错误。
- 缺乏类型检查:宏不进行类型检查,因此在使用宏时需要自行确保类型匹配,否则可能导致运行时错误。
为了解决这些问题,C++提供了一些替代方案来减少宏的使用:
- 使用const和constexpr:C++中的const和constexpr关键字可以用于定义常量,避免了宏定义常量的麻烦,并且提供了类型安全和编译期计算的优势。
- 使用内联函数:C++的内联函数可以取代宏,以提高代码的可读性和类型安全性。内联函数在编译时展开,避免了宏带来的副作用和类型不匹配的问题。
- 使用模板:模板是C++的强大特性之一,可以实现类型安全的泛型编程。通过模板,可以避免使用宏进行代码的泛化。
6 关键字volatile的作用⭐⭐⭐⭐⭐
1 并行设备的硬件寄存器。存储器的硬件寄存器通常加volatile,因为寄存器随时可以被外设硬件修改。当声明指向设备寄存器的指针时一定要用volatile,它会告诉编译器不要对存储在这个地址的数据进行假设。
2 一个中断服务程序中修改的供其他程序检测的变量。volatile提醒编译器,它后面定义的变量随时都有可能改变。因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序跟新的话,将出现不一致的现象。
3 多线程应用中被多个任务共享的变量。单地说就是防止编译器对代码进行优化。
7 关键字static的作用⭐⭐⭐⭐⭐
1 在函数体中,只会被初始化一次,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2 在模块内(函数体外),一个被声明为静态变量可以在模块内所用函数访问,但不能被模块外其他函数访问。它是一个本地的全局变量(只能在当前文件使用)。
3 在模块内,一个被声明为静态的函数只可被这一模块内的其他函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用(只能被当前文件使用)。
其中对于所有的对象(不仅仅是静态对象),初始化都只有一次,而由于静态变量具有记忆功能,初始化后,一直没有被销毁,都会保存在内存区域中,所以不会再次初始化。存放在静态区的变量的生命周期一般比较长,他与整个程序同生死,同存亡,所以只需要初始化一次。
而auto变量,即自动变量,由于它存放在栈区,一旦函数调用结束,就会立即被销毁。
8 关键字const的作用⭐⭐⭐⭐⭐
1 定义变量(全局变量或者局部变量)为常量。
2 修饰函数的参数,表示在函数体内不能改变这个参数的值。
3 修饰函数的返回值。
a 如果给用const修饰返回值的类型为指针,那么函数返回值的内容是不能被修改的,而这个返回值只能赋给被const修饰的指针。
b 如果用const修饰普通的返回值,如返回int变量,由于这个返回值是一个临时变量,在函数调用结束后这个临时变量的生命周期也就结束了,因此这个返回值修饰为const是没有意义的。
4 节省空间,避免不必要的内存分配。
在啥情况下使用const关键字
1 修饰一般变量。一般常量是指简单类型的常量。
2 修饰常数值。
3 修饰常对象。常对象是指对象常量。
4 修饰指针。
const int*p; //常量指针,指向常量的指针。即p指向的内存可以变,p指向的数值内容不可变 int const*p; //同上 int*const p;//指针常量,本质是一个常量,而用指针修饰它。 即p指向的内存不可以变,但是p内存位置的数值可以变 const int* const p;//指向常量的常量指针。即p指向的内存和数值都不可变
5 修饰常引用。被const修饰的引用变量为常引用,一旦被初始化,就不能指向其他对象了。
6 修饰函数的常参数。const修饰符也可以修饰函数的传递参数
7 修饰函数的返回值。const修饰符也可以修饰函数的返回值,表明该返回值不可被改变。
8 在另一连接文件中引用const常量。
说说const和define的区别
const用于定义常量;而define用于定义宏,而宏也可以用于定义常量。都用于常量定义时,它们的区别有:
const生效于编译的阶段;define生效于预处理阶段。
const定义的常量,在C语言中是存储在内存中、需要额外的内存空间的;define定义的常量。
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
该专栏面向嵌入式开发工程师、C++开发工程师,包括C语言、C++,操作系统,ARM架构、RTOS、Linux基础、Linux驱动、Linux系统移植、计算机网络、数据结构与算法、数电基础、模电基础、5篇面试题目、HR面试常见问题汇总和嵌入式面试简历模板等20篇文章。超全的嵌入式软件工程师笔试面试题目和高频知识点总结!招聘so easy。