北京君正笔试题

常规题

用预处理指令#define写一个两个变量相乘的宏函数

答案

#define MULT(A, B) (A) * (B)


用预处理指令#define声明一个常数,用以表明 8GByte 的常数

答案

#define 8GNUM 8*1024*1024


关键字volatile有什么含意?

答案

volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改。volatile 提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如 果没有 volatile 关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。所以遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
参考


指出下面两个指针的含义?

const int* a

int* const a

答案
  • const int* a:表示a是一个指针,它指向的是一个常量int类型的数据,不可更改。
  • int* const a:表示a是一个常量指针,它指向的int类型的数据是可以改变的,但是a本身是一个常量指针,不能改变其指向


a=3,b=5,不用第三变量temp,对ab的值进行交换

答案
1
2
3
a = a ^ b;
b = a ^ b;
a = a ^ b;


C 语言中,运算对象必须是整型数的运算符的有 ( )

A、% B、/ C、%和/ D、*

答案

A


常见的 STL 容器有哪些?

答案
  1. vector可变大小的数组,可以保存任意类型的数据;
  2. list双向链表,可以快速插入和删除元素;
  3. deque双端队列,可以快速在两端添加或移除元素;
  4. stack后进先出(LIFO)的数据结构;
  5. queue先进先出(FIFO)的数据结构;
  6. priority_queue优先队列,根据元素的优先级来排序;
  7. set集合,保存唯一无序的元素;
  8. map映射,以 key-value 的形式存储元素;
  9. unordered_set无序集合,保存唯一无序的元素;
  10. unordered_map无序映射,以 key-value 的形式存储元素。


设计模式懂嘛,简单举个例子

答案

设计模式是指在特定情况下,为解决某一特定问题而提出的模式化的、可重复使用的解决方案。常见的设计模式有单例模式工厂模式模板模式等等。

例如:工厂模式:工厂模式是一种用于创建对象的设计模式,其目的是将具体的创建逻辑从客户端代码中分离出来,从而使得同一个工厂可以创建出不同类型

的对象。例如,一个汽车工厂可以创建出轿车、卡车和SUV等不同类型的汽车。


C++中引用和指针的区别

答案

C++中引用和指针的区别主要有以下几点:

  1. 引用是一种符号而指针是一种变量
  2. 引用必须在声明的时候初始化而指针可以在任何时候进行初始化
  3. 引用只能绑定到一个对象而指针可以在任何时候指向任何对象
  4. 引用不能为空而指针可以为空
  5. 引用的大小和原对象的大小是一样的而指针的大小取决于指针变量的类型
  6. 引用可以用来改变原始值而指针只能用来改变指向的对象
  7. 引用更安全指针更灵活


编写my_strcpy函数,实现与库函数strcpy类似的功能,不能使用任何库函数

答案
1
2
3
4
5
6
7
8
9
10
11
char *my_strcpy(char *dest, const char *src)
{
int i = 0;//循环变量
for(i = 0; src[i] != '\0'; i++)
//可以遍历src指向字符串的所有有效字符
{
dest[i] = src[i];//循环拷贝有效字符
}
dest[i] = src[i];//循环之后,拷贝最后的'\0'
return dest;
}


完成一个sort的程序代码

1
2
3
4
5
6
7
8
9
10
11
void sort(int array[]){

}

int main()
{
int array[] = (4556762341342323}; //数字任意给出
sort(array);
return 0;
}

答案
1
2
3
4
5
6
7
8
9
10
11
12
void sort(int array[]) {
int i, j;
for (i = 0; i < sizeof(array) / sizeof(array[0]) - 1; i++) {
for (j = 0; j < sizeof(array) / sizeof(array[0]) - i - 1; j++) {
if (array[j] > array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}


附加题

DMA 的作用,在使用 DMA 时需要注意哪些操作。

  1. 画出一个 CPU 使用 DMA 传输数据的数据流框图。
  2. 说明数据一致性,以及如何保证数据一致性
答案

DMA的数据一致性是指在DMA传输过程中,源和目的地中的数据保持一致。为了保证数据的一致性,DMA控制器通常会进行一些特定的操作,比如实现缓冲器或缓存的技术,以确保数据的完整性和一致性。另外,DMA控制器还可以采用一些错误检测技术,比如校验和等,来检测数据传输过程中是否出现错误。

列举所知道的 Linux 内核内存申请方法,并尽可能的描述不同申请方法的特点

答案

inux内核提供了多种内存申请方法,比如kmallocvmalloc__get_free_pages__get_dma_pages等等,它们有以下几种不同的特点:

  1. kmalloc:该函数可以用于申请任意大小的内存块,但大小不能超过一定的限制(通常在128KB以下),并且可以用于非DMA内存申请;
  2. vmalloc:该函数可以用于申请任意大小的内存块,且大小没有上限,但由于它使用的是虚拟的内存空间,所以速度会比较慢;
  3. __get_free_pages:该函数可以用于申请大小为一页(4KB)的内存块,并且可以用于非DMA内存申请;
  4. __get_dma_pages:该函数可以用于申请大小为一页(4KB)的内存块,并且只能用于DMA内存申请。


用户空间申请内存的方法和特点。

答案

一、方法:

1、使用 malloc() 函数,该函数用于动态分配内存,可以用来申请任意大小的内存空间;

2、使用 calloc() 函数,该函数可以根据需要申请指定大小的内存空间,并将分配的内存空间全部初始化为 0;

3、使用 realloc() 函数,该函数可以改变已经分配的内存空间的大小,重新分配更大的内存空间,或者释放多余的内存空间。

二、特点:

1、用户空间内存申请的方式灵活,可以满足不同的需求;

2、内存申请的程序可以在任何地方运行,只要有可用的内存空间;

3、用户空间内存申请的效率比系统空间内存申请要高;

4、内存的大小可以根据需要动态调整,可以有效地利用内存空间;


描述 TLB 的原理和作用,说明内核是如何使用 TLB 的

答案

TLB(Translation Lookaside Buffer)是一种高速缓存,用于翻译虚拟地址到物理地址。TLB 将虚拟地址映射到物理地址的过程称为“地址翻译”。TLB 中的每一项都包含了虚拟地址和相应的物理地址。当 CPU 尝试访问一个虚拟地址时,它会首先检查 TLB,如果找到相应的物理地址,就可以直接访问;如果没有找到,就会调用内存管理单元(MMU),MMU 会从页表中查找相应的物理地址,并将其添加到 TLB 中,之后 CPU 会使用该物理地址来访问内存。

内核是通过将虚拟地址映射到物理地址来使用 TLB,即将虚拟地址映射到物理地址,以便在访问内存时,可以减少对内存管理单元(MMU)的调用,从而提高效率。内核会首先检查 TLB 中是否有虚拟地址对应的物理地址,如果有,就会直接使用该物理地址;如果没有,就会调用 MMU,从中查找相应的物理地址,并将其添加到 TLB 中,之后内核会使用该物理地址来访问内存。


基于以下内核打印作答

  1. 从以上 Log 信息,分点列举你认为的关键信息,分析系统可能出现了什么问题系统当前做出了什么响应?
  2. 在实际工作中,写出你有哪些手段去分析该问题。
答案

#define MULT(A, B) (A) * (B)


请简要描述中断的作用,并尽可能详细深入地描述中断从触发到处理的过程。

答案

中断是用来处理硬件或软件异常的一种机制,它可以帮助系统检测到并处理硬件或软件错误,以便系统能够恢复正常运行。

所谓这个中断在arm这个架构下分了三层。所谓中断,就是cpu感知外部事件变化的一种方式。除了中断,还有轮询,还有DMA,但是中断是我们用的最多的一种方式。

中断它和轮询相比,不需要cpu不断的去检测或者探测外设的状态。所谓中断就是指的是当外设或者系统定时器,产生了一个中断信号将来要上报给Cpu,但是cpu不会去直接接收中断信号,cpu让中断控制器去接收。

之前在玩STM32的过程中发现不是说所有的这些外设都是直接的连接到中断控制器上的,有一部分是通过外部中断连接到了中断控制器上,比如说我们的gpio的管脚是连接到了外部中断上,将来外部中断是不是再连接到中断控制器上。也就是说将来我的串口、i2C的那些设备可以直接给中断控制器发送中断信号,但是我gpio类的接口我得通过外部中断去上报这终端信号。再有我中断控制器可以进行中断屏蔽。我可以屏蔽你的这些外部中断,也可以不屏蔽,如果不屏蔽中断,中断信号就可以往上报了,往上报就报给中断控制器。

中断控制器完成两个事,第一:为所有连接到这上面的中断源分配一个中断号,而且这个中断号跟异常向量表里边的顺序是息息相关的,不能轻易更改,要想改的话,也得在最后边改。如果我一改,那个顺序乱了就接收不了中断。这是中断控制器要干的。为所有连接上来的中断源分配一个中断信号。

第二:还要处理中断优先级的问题。arm在提供解决方案的时候,给咱们规定了一个8位的寄存器,也就是256个优先级,256个可设置的优先级,还有三个固定的NMI,Reset还有一个硬件失效,stm32只支持16个中断优先级。
中断优先级又分一个组优先和子优先,所以我就需要到时候去配组优先和子优先的配比问题,要设置配比。到这儿,中断控制器就搞定了中断信号,比如说按键按下了以后,这个中断信号就进入了中断控制器,报给了Cpu核。至于说cpu核心怎么去处理,那是不是就cpu核的事了。

总结

君正面试题个人感觉比较全面,C/C++基础、Linux、硬件原理还有考察纠错能力。如果想要全部写出来难度还是有的,特别是附加题,想要精炼简洁地写出题目要点,还是要下很多的功夫。

2021年的面试题大相径庭,就是常规题多了一道对于static的理解这里先不整理,后续面试题再进行整理。