嵌入式软件面试2023经典题汇总C语言一
【嵌入式软件面试】(2023)经典题汇总(C语言)【一】
嵌入式软件工程师面试经典提问汇总
首先,寻找嵌入式软件工程师的工作需要具备的知识非常多,现如今嵌入式相关岗位的竞争非常激烈,一场的优秀的面试可以提升面试官对自己认可度,提升自己拿offer的概率。下面列出来总结的20个经典面试题目供大家参考。题目内容根据经验整理总结,如有错误之处还请批评指正。如有侵权,请联系删除!
目录
嵌入式软件工程师需要掌握的知识:
熟练掌握C/C++编程语言:嵌入式软件开发主要使用C/C++语言(还需要掌握一些Python),因此需要熟练掌握这些语言的语法、数据结构和算法,具备一定的编程经验。
熟悉嵌入式系统架构和硬件知识:嵌入式软件开发需要对嵌入式系统的体系结构和硬件知识有一定的了解,才能进行底层驱动程序的编写和调试。
熟悉嵌入式开发和调试工具:嵌入式软件开发需要使用各种嵌入式开发和调试工具,如Keil、AD、IAR、CubeMX、Tasking、J-Link等,需要熟悉这些工具的使用方法和调试技巧。
熟悉嵌入式通信协议:嵌入式系统通常需要与外部设备进行通信,因此需要熟悉各种嵌入式通信协议,如UART、SPI、I2C、CAN、Moudbus等。
熟悉嵌入式操作系统:嵌入式系统通常需要使用RTOS,因此需要熟悉各种嵌入式操作系统,如FreeRTOS、uC/OS等。
6.熟悉Linux操作系统:熟悉Linux系统编程,能够使用vim开发工具,具备使用Linux开发项目的经验。
嵌入式软件工程师经典面试题目:
1、关键字 static 的作用是什么?
2 、.h 头 文件中的 ifndef/define/endif 的作用?
答:防止该头文件被重复引用。
3、#include 与 #include “file.h”的区别?
答:前者是从 Standard Library 的路径寻找和引用 file.h,而后者是从当前工作路径搜寻并引用 file.h。
4、全局变量和局部变量在内存中是否有区别?如果有,是什么区别?
答 :全局变量储存在静态数据区,局部变量在堆栈中。
即
全局变量保存在内存的全局存储区,占用静态的存储单元;局部变量保存在栈中,只有在所在函数被调用时才动态地为变量分配存储单元。
扩展 :
C语言经过编译之后将内存分为以下五个区域
(
1
)
.栈:由编译器进行管理,自动分配和释放,存放函数调用过程中的各种参数,局部变量,返回值及函数返回地址。操作方式类似数据结构中的栈。
(
2
)
.堆:用于程序动态申请分配和释放空间。C语言中的malloc和free,C++中的new和delete均是在堆中进行的。正常情况下,程序员申请的空间在使用结束后应该释放,若程序员没有释放空间,则程序结束时系统自动回收。注意,这里的”堆”并不是数据结构中”堆”。
(
3
)
.全局(静态)存储区:分为DATA段和BSS段。DATA段(全局初始化区)存放初始化的全局变量和静态变量;BSS段(全局未初始化区)存放未初始化的全局变量和静态变量。程序运行结束时自动释放。其中,BSS段在程序执行之前会被系统自动清零,所以未初始化全局变量和静态变量在程序执行之前已经为0。
(
4
)
.文字常量区:存放常量字符串。程序结束后由系统释放。
(
5
)
.程序代码区:存放程序的二进制代码。
显然,C语言中的全局变量和局部变量在内存中是有区别的。C语言中的全局变量包括外部变量和静态变量,均是保存在全局存储区中,占用永久性的存储单元;局部变量即自动变量,保存在栈中,只有在所在函数被调用时才由系统动态在栈中分配临时性的存储单元。
5、堆栈溢出一般是由什么原因导致的? (你有没有遇到过?)
答 :
(
1
)
没有回收垃圾资源
(
2
)
层次太深的递归调用
内存分配中的堆区和栈区
堆与栈的区别?
答:(1)栈的空间是系统自动分配和回收,堆的空间是用户手动分配回收(malloc,calloc,realloc,free)
(2)栈的空间较小,堆的空间较大
(3)栈的地址空间往地址向下增长,堆的地址空间是由低地址到高地址
(4)栈的存储效率更高
6、SPI总线协议
7、I2C总线协议
8、递归函数定义没有问题,递归深层次后易引发什么问题?
答:(1)影响执行效率
(2)栈溢出。
因为每一次调用函数是,栈区都要给函数分配空间,而且上一次调用并没有结束,调用的次数太多,栈区的内存不够分配了,便会出现栈溢出的情况。
9、循环控制条件关键字goto被经常使用,但是goto的使用场合为什么受到局限?
答:
因为goto会破坏程序的栈逻辑。
循环控制条件关键字goto的使用场景?
答:(1)用来跳出死循坏;(2)打印错误;
10、预编译,编译,汇编,链接都做了什么?
答:预处理,展开头文件/宏替换/去掉注释/处理条件编译 (test.i main .i)
编译, 检查语法,生成汇编文件 ( test.s main .s)
汇编, 汇编代码转换机器码 (test.o main.o)
链接, 链接到一起生成可执行程序 a.out
11、C语言关键词volatile用法
答:volatile用于声明变量时的使用的限定符。它告诉编译器该变量值可能随时发生变化,且这种变化并不是代码引起的。给编译器这个暗示是很重要的。
volatile 重点
只要变量可能被意外的修改,就需要把该变量声明为volatile。实际应用中,只有三种类型数据可能被修改。
外设寄存器地址映射
在中断服务程序中修改全局变量
在多线程、多任务应用中,全局变量被多个任务读写
12、extern关键字详解
答:extern使用方法总结!
一、问题:extern的问题在于不知道这个关键词出现的时候到底是声明还是定义?
二、详解:1、函数的声明extern关键词是可有可无的,因为函数本身不加修饰的话就是extern。但是引用的时候一样需要声明的。
2、全局变量在外部使用声明时,extern关键字是必须的,如果变量没有extern修饰且没有显式的初始化,同样成为变量的定义,因此此时必须加extern,而编译器在此标记存储空间在执行时加载内并初始化为0。而局部变量的声明不能有extern的修饰,且局部变量在运行时才在堆栈部分分配内存。
3、全局变量或函数本质上讲没有区别,函数名是指向函数二进制块开头处的指针。而全局变量是在函数外部声明的变量。函数名也在函数外,因此函数也是全局的。
4、谨记:声明可以多次,定义只能一次。
5、extern int i; //声明,不是定义
int i; //声明,也是定义
13、const-static-指针-内存
答:const是定义静态变量,变量不可修改
const意味着"只读"就可以了
(1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
static将变量的作用域限制到此文件或函数
static的作用
1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
这句话的意思就是在 static 修饰局部变量
的时候就算函数结束,局部变量依然存在且维持其值不变,提升了局部变量的生存周期和全局变量一样长。
2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
这句话的意思就是 static 在修饰全局变量时,只能被当前.c文件下的函数使用,不能被其他.c文件调用。
3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
被static修饰的函数只能在当前.c这个文件内被访问,在其他文件内则无法访问此函数,否则会造成 undefined reference(找不到函数定义)错误。
const关键字的作用
是所修饰的变量除了在其初始化时其他时间不能被修改,同时const在修饰指针时所处的位置不同会产生不同的效果。一句话就是const在修饰指针时作用是修饰在其位置之后的变量,例const int *a; 那么const修饰的就是 a ,a代表的是空间不是地址,那么就是a不可以被改变,但是地址可以被改变,int * const a; const后面是 a ,a是指针,所以就代表着地址不可以被修改,但是a可以被修改。
其中int const static三个关键词的地位相同,顺序可交换,即int const num;和 const int num 一个意思。
14 、int变量未初始化的默认初值,和变量的类型有关
答:(1)局部变量,在未初始化情况下,初值为随机值。C规范对该初值并没有做规定,具体实现由编译器决定。如VC/VS等编译器,会将初始值值为0xCCCCCCCC, 而GCC等编译器则是不可预知的随机值。
(2) 静态局部变量,即带static修饰的局部变量。
全局变量和静态全局变量,即定义在函数外,不属于任何一个函数的变量。
这几种默认初值为0。
15、static和volatile的使用
答:1.static保证唯一性,就是在主内存中是唯一的变量。
2.volatile是保证可见性,就是指在工作线程和主内存的数据的一致性,改变了工作线程中volatile修饰的变量,那么主内存也要发生更新。
所以,volatile和static一起使用不矛盾。因为static修饰只能保证在主内存的唯一性,如果涉及到其他工作线程,改变参数可能就会导致static修饰的变量的内容无法同步,所以static和volatile可以一起使用,因为他们管的地方是不一样的,互不影响。
16 、【C/C++】结构体和联合体的区别?
答:联合体
用途:使几个不同类型的变量共占一段内存(相互覆盖)
结构体 是一种构造数据类型
用途:把不同类型的数据组合成一个整体——-自定义数据类型
Struct与Union主要有以下区别:
struct和union都是由多个不同的数据类型成员组成, 但在任何同一时刻, union中只存放了一个被选中的成员, 而struct的所有成员都存在。在struct中,各成员都占有自己的内存空间,它们是同时存在的。一个struct变量的总长度等于所有成员长度之和。在Union中,所有成员不能同时占用它的内存空间,它们不能同时存在。Union变量的长度等于最长的成员的长度。
对于union的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于struct的不同成员赋值是互不影响的。
所占内存——结构体所有成员变量所占内存之和,联合体最长变量的内存大小
struct各个变量赋值互不影响,独自占有内存空间(自定义数据类型),union某一变量的改变会覆盖处于内存起始位置的变量值(相互覆盖)
union各个成员共用一段内存地址,在不同的时间保存不同类型、不同长度的变量;当union被声明时, 编译器自动产生一个变量,该变量长度为union中最长变量的长度
17 、进程和线程的区别
1.根本区别:进程是操作系统进行资源分配的最小单元,线程是操作系统进行运算调度的最小单元。
2.从属关系不同:进程中包含了线程,线程属于进程。
3.开销不同:进程的创建、销毁和切换的开销都远大于线程。
4.拥有资源不同:每个进程有自己的内存和资源,一个进程中的线程会共享这些内存和资源。
5.控制和影响能力不同:子进程无法影响父进程,而子线程可以影响父线程,如果主线程发生异常会影响其所在进程和子线程。
6.CPU利用率不同:进程的CPU利用率较低,因为上下文切换开销较大,而线程的CPU的利用率较高,上下文的切换速度快。
7.操纵者不同:进程的操纵者一般是操作系统,线程的操纵者一般是编程人员。
资源分配不同。从线程和进程的定义可以看出,进程拥有独立的内存和系统资源,而在一个进程内部,线程之间的资源是共享的。系统不会为线程分配资源。
工作效率不同。进程拥有系统资源,在进程切换的时候,操作系统需要保留进程占用的资源;而线程的切换不需要保留系统资源,切换效率远远高于进程。线程较高的切换效率提高了数据处理的并发能力。
执行方式不同。线程有程序运行的入口地址,但是线程不能独立运行。由于线程不占有系统资源,所以线程必须存放在进程中。进程可以被操作系统直接调度。在一个进程内部有多个线程可以共享资源和调度。不同进程之间的线程资源是不能直接共享的。进程可以被认为是线程的集合。
18 、Cortex-M3/M4芯片启动流程
1、启动模式
BOOT0拉低,BOOT1任意,芯片从 Main Flash memory (0x08000000)启动
BOOT0拉高,BOOT1拉低,芯片从系统存储 System Memory 启动
BOOT0拉高,BOOT1拉高,芯片从 SRAM (0x20000000)启动
2、烧录方式(ISP ICP IAP的区别)
ISP:Boot0=1 Boot1=0,系统从System Memory启动,此处是ST公司在芯片出厂时固化好的一块程序,也可以叫做BootLoader或ISP程序,通过此程序,可以将串口下载进板子的固件程序(hex文件)写入到Main memory中,然后再将Boot0拉低,重启即可运行Main memory出的代码。
ICP:Boot0=0,BOOT1=x,系统直接从Main memory启动,通过JTAG或SWD可以直接将代码烧录到Main memory
IAP:将Mian memory分成两部分,第一部分可以再写一个bootloader程序,它的作用是通过有线或者无线的方式获取外部的固件升级包,然后再写入到第二部分的flash中,重定位中断向量表的地址和更改PC指向,这样可以自动实现板子程序的自动升级
3、中断向量表
4、SystemInit
5、__main
ARM启动过程
我们的ARM开发板有多种启动方式,可以从NORFlash启动、可以从NANDFlash启动、也可以从SD卡启动。当系统上电后,ARM会从U-boot启动,然后U-boot将自身代码拷贝到内存DDR SDRAM中,接着PC指针指向内存,从内存中不断地取指令、翻译指令、运行指令。
19、 GCC 编译
Linux下gcc编译器的使用
什么是交叉编译?
答:
在一种计算机环境中运行的
编译程序
,
能编译出在另外一种环境下运行的代码
,这个编译过程就叫 交叉编译
。简单地说,就是在一个平台上生成另一个平台上的可执行代码。
20、sizeof()与strlen()的区别?
(1)sizeof是运算符,计算能容纳实现所建立的最大对象的字节大小,参数可以是数组、指针、类型、对象、函数等;
(2)strlen是函数,功能是返回字符串的长度,参数必须是字符型指针(char*)。