嵌入式软件面试题,持续更新......

前言

作为嵌入式软件工程师,跳槽的时候通常会先来一份C/C++面试题。这里总结一下自己或者网友遇到的C/C++面试题,供大家参考下。

实例

1、零值比较

//bool类型的零值比较
bool flag;

if(flag){
    if(!flag){

    }
}

//float类型的零值比较
float x;
const float EPSINON = 0.00001

if((x >= -EPSINON) && (x <= EPSINON))

//指针类型的零值比较
char *p;

if(p == NULL){
    if(p != NULL){
        
    }
}

2、sizeof

sizeof用于计算一个变量或者类型所占的内存大小,返回单位为字节;

char str[] = "Hello";
char *p = str;
int n = 10;

sizeof(str) = 6;//Hello占5个字节,结尾结束符占一个字节
sizeof(p) = 4;//一般32位机器,指针类型变量占4个字节
sizeof(n) = 4;//一般32位机器,int类型变量占4个字节

void Func(char str[100]){
    sizeof(str) = 4;//函数未被调用时,形参不分配内存;函数调用时,形参被分配内存,直到函数执行完            
                    //毕;传递的str[100]是个数组,因此传入函数的形参可以理解为str[100]的地址,
                    //所以占4个字节
}

void *p = malloc(100);
sizeof(p) = 4;//同指针类型解释

3、变量不同类型的定义

int a;//一个整型数
int *a;//一个指向整型数的指针
int **a;//一个指向指针类型的指针,其指向的指针指向一个整型数
int a[10];//一个有10个int型元素的数组
int *a[10];//一个有10个指向int型数据的指针类型元素的数组
int (*a)[10];//一个数组指针,指向有10个int型元素的数组
int (*a)(int);//一个函数指针,该函数有一个int型参数并返回一个int型数据
int (*a[10])(int);//一个有10个指针的数组,该指针指向一个函数,该函数有一个int型参数并返回一个int 
                  //型数据

4、

5、

6、

7、

8、

9、

10、

11、

12、

13、关键字volatile有什么含义?并给出三个不同的例子。

A、一个定义为volatile的变量是说这个变量可能会被意外改变,这样,编译器就不会去假设这个变量的值。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里面的备份。

B、例子

1)并行设备的硬件寄存器(如:状态寄存器)

2)一个中断服务子程序中会访问到的非自动变量

3)多线程应用中被几个任务共享的变量

14、

15、头文件中的ifndef/define/endif是为了防止该头文件被重复引用。

16、#include <filename.h>和#include "filename.h"有什么区别?

#include <filename.h>是告诉编译器,优先从标准库开始搜索filename.h;

#include "filename.h"是告诉编译器,优先从用户的工作路径开始搜索filename.h;

17、const有什么作用?

(1)const可以修饰常量

(2)被const修饰为只读的变量、函数的参数、返回值会受到保护,可以防止被意外地改变,能提高程序的健壮性。

18、简单说明一下关键字static和volatile的作用?

(1)static修饰一个局部变量时,生命域是全局的,作用域是局部的;

static修饰一个C源文件内的全局变量时,生命域是全局的,作用域减小只作用于本文件;

static修饰一个函数时,生命域是全局的,作用域减小只作用于本文件;

(2)volatile表示被修饰的变量是易变的,警告编译器每次都要从变量的内存地址读取,而不是读寄存器的备份;

(3)const和volatile可以同时修饰一个变量,例如:只读的状态寄存器;

const和static不可以同时修饰一个变量,因为C++编译器在实现const的成员函数的时候为了确保该函数不能修改类的实例的状态,会在函数中添加一个隐式的参数const this *。但当一个成员为static的时候,该函数是没有this指针的。也就是说此时static的用法和static是冲突的。

19、链表和数组的区别?

(1)在内存中,数组是一块连续的区域,链表则是随机的离散区域。

(2)数组的查找读取数据块,因为它是连续的,知道一个数据地址就能找到对应的数据;但是数组的数据插入和删除效率低,对内存空间的要求高,必须由足够的连续空间,有可能会造成内存浪费。

(3)链表的数据插入和删除效率高,内存利用率高且拓展灵活;但是查找读取数据慢,必须使用遍历的方式查找数据。

20、怎么理解中断的过程?

中断的过程可以顺序分为4个部分:入栈,取向量,更新寄存器和异常返回;

(1)入栈:保存现场,数据总线依次把xPSR,PC,LR,R12和R3~R0压栈;

(2)取向量:指令总线从向量表取出服务函数入口地址;

(3)更新寄存器:更新堆栈指针SP,PSR,PC(PC指向服务函数入口地址),LR;

(4)异常返回:恢复现场(数据出栈),更新NVIC寄存器;

Tips:LR(R14)是存储返回地址,所以当调用一级深度的子程序返回时,可以直接读取LR中的返回地址而不用去读内存;当多级深度时就需要把上一级的返回地址压入堆栈;

21、中断服务函数能有输入参数和返回值吗?为什么?

中断服务函数不能有输入参数和函数返回。因为中断从本质来说是硬件外设自动产生的一种电信号,由硬件自身调用,没有程序去传递参数给硬件,硬件也不会接收参数。再者中断处理要越快越好以确保实时操作系统内的实时性,如Linux系统的硬中断和软中断机制。

22、怎么理解信号量、互斥信号量?

(1)信号量就是一个用来表示某个资源被占用情况的标志。

信号量的功能:A、实现任务间的同步;B、管理多个共享资源;

(2)互斥信号量就是一个值只有0和1两种情况的二值信号量,实现对资源的互斥访问。

互斥信号量的功能:A、防止任务优先级反转问题;

23、简单说一下ARM处理器架构和C51处理器架构的区别?

(1)ARM是32位RISC(精简指令集)微处理器架构的总称;C51是8位CISC(复杂指令集)微处理器架构的总称;

(2)ARM采用冯诺依曼或者哈佛的内核结构,如Cortex-M3就采用的是哈佛结构;C51采用冯诺依曼的机构内核;

24、简单说一下,UCOS II和UCOS III的区别?

(1)UCOS II只有0~63个优先级,而且优先级不能重复;UCOS III允许几个任务使用同一个优先级,在同一个优先级里面,支持时间片调度。

(2)UCOS III允许用户在程序运行中动态配置实时操作系统内核资源,避免用户在编程中出现资源不够用的问题。

(3)UCOS II最大支持256个任务;UCOS III支持任意的任务、信号量、消息列表和内存块容量,只受限于用户CPU可以使用的RAM容量;

25、简单说一下,UCOS II中消息队列、消息邮箱和信号量的区别?

(1)用信号量进行行为同步时,只能提供同步的时刻信息,不能提供内容信息。若被控制方要求得到控制方的内容信息时,可以使用消息邮箱或消息队列。

(2)但由于消息邮箱里面只存放一条消息,所以使用消息邮箱进行任务同步时,需要满足一个条件:消息的产生速度总要慢于消息的消费速度,即被控制任务总是在等待消息,否则会导致消息丢失。

(3)若遇到出现消息的产生速度可能快于消息的消费速度的情况时,则可以使用比消息邮箱更为强大的消息队列,由于消息队列可以存放多条消息,所以消息队列能够有效解决消息的临时堆积问题。但消息队列的使用仍然需要满足一个条件:消息的平均产生速率比消息的平均消费速率低,否则再长的消息队列也会溢出。

26、简单说一下,可重入函数和不可重入函数?

(1)可重入函数:重入意味着这个函数可以重复进入,可以被并行调用,可以被中断,它只使用自身栈上的数据变量,它不依赖于任务环境,在多任务调度过程中,是安全的,不必担心数据出错;

(2)不可重入意味着不可被并行调度,否则会产生不可预料的结果,这些函数体内一般使用了全局变量、静态(static)的数据结构,使用了malloc或者free函数,使用了标准I/O函数等等;

(3)可理解为两者互斥,凡不是不可重入函数的就是可重入函数;

猜你喜欢

转载自blog.csdn.net/SammySum/article/details/105980142
今日推荐