« 上一篇下一篇 »

面试总结(2018年)

一、C语言相关

1、strcpy()函数、malloc()函数用法;

strcpy()函数原型:

char *strcpy(char *dst, const char *src);

strcpy()只用于字符串复制,而且它不仅复制字符串内容,还会复制字符串的结束符。它的返回值为dst。为什么需要返回dst呢?目的是为了实现链式操作,所以将目的地址返回。比如下述代码:

int length = strlen( strcpy(str, “Hello World”) );

链式操作的定义:编程语言中的链式操作是利用运算符进行的连续运算(操作),它的特点是在一条语句中出现两个或两个以上相同的操作符,如连续的赋值操作、连续的输入操作、连续的输出操作、连续的相加操作等都是链式操作的例子。

malloc()函数原型:

extern void *malloc(unsigned int num_bytes);

malloc()用于申请n字节的内存空间,它的参数值即为申请到的字节数。如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。

2、函数指针、指向二维数组的指针、指针数组;

函数指针:

int f(int x);
int (*pf)(int) = &f;

其中pf为指针函数的指针,该函数的返回值为int。

指向二维数组的指针:

int a[3][5];
int (*p)[5] = a;

其中p为指向整型数组a的指针。

指针数组:

int *p[10];

下标引用的优先级高于间接访问,所以p为一个数组,它的元素类型为指向整型的指针。

3、数组的地址;

比如 int a[5] = {1, 2, 3, 4, 5}; int *p = &a + 1; int *q = a + 1的区别;

如上所示数组,&a+1表示数组a的首地址再加上整个数组大小的偏移,这个偏移量是4字节*5个元素=20,所以*p的值为数组元素5再后面一个位置空间所存储的值,此值无明确定义,而*(p-1)的值则为数组最后一个元素5;而a + 1则为数组a的首元素的首地址再加上1个元素大小的偏移,所以*q的值为a[1]的值2。

4、前缀++、后缀++;

先来看个题目:

Question.请写出下列代码的输出内容。

#include <stdio.h>
main()
{
    int a, b, c, d;
    a = 10;
    b = a++;
    c = ++a;
    d = 10*a++;
    printf("b, c, d: %d, %d, %d", b, c, d);
    return 0;
}

上次去面试,这个题目竟然做错了,然后被面试官当场指出,叫我好好补补基础。唉,真是大意,我竟然写了:b, c, d: 11, 12, 120 。亏我以前还专门捧着《C和指针》专门研究过++运算符呢,失误失误。

这里再强调下++运算符的运算过程。总的说来,不管是前缀++还是后缀++,它总会产生一份目标变量的拷贝,而我们最后用于运算的,就是这份拷贝的值。不同之处在于,这份拷贝产生的时间。

前缀++:例如有操作“++cp”,则其运算过程为:(1)首先++操作符增加cp的值。(2)然后++操作符产生增值后cp的一份拷贝。而我们最后使用的,就是这份拷贝,而前缀++是先对cp做增值操作,然后再产生一份拷贝,所以我们最后使用的值便是cp加1后的值。

后缀++:例如有操作“cp++”,则其运行过程为:(1)首先++操作符产生cp的一份拷贝。(2)然后++操作符增加cp的值。由于我们最后使用的是拷贝,而拷贝过程发生在cp增值之前,所以我们最后使用的值仍为cp原来的值,而操作结束后cp的值比原来的值(也就是拷贝的值)多1。

所以上题中正确答案很明显为:b, c, d: 10, 12, 120 。

5、宏定义:如何通过宏定义 swap(x, y)实现两个参数的值的交换;

利用位运算符:

#define SWAP(a, b) {a = a^b; b= a^b; a= a^b;}


二、数据结构与算法

1、排序(至少要非常熟练一种排序算法,尤其是冒泡排序);

冒泡排序(Bubble sort)是一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。示例代码如下。

#define MAXSIZE 10    /* 用于要排序数组个数最大值,可根据需要修改 */
typedef struct
{
    int r[MAXSIZE];    /* 用于存储要排序数组*/
    int length;        /* 用于记录顺序表的长度 */
}SqList;

/* 交换L中数组r的下标为i和j的值 */
void swap(SqList *L, int i, int j)
{
    int temp = L->r[i];
    L->r[i] = L->r[j];
    L->r[j] = temp;
}

/* 对顺序表L作冒泡排序 */
void BubbleSort(SqList *L)
{
    int i, j;
    for(i = 0; i < L->length - 1; i++)
    {
        for(j = L->length-2; j >= i; j--)    /* 注意j是从后往前循环 */
        {
            if (L->r[j] > L->r[j+1])    /* 若前者大于后者 */
            {
                swap(L, j, j+1);        /* 交换L->r[j]与L->r[j+1]的值 */
            }
        }
    }
}

2、链表(链表的结构)、二叉树和图(至少要清楚概念,可以自己简单写写代码加深理解);

链表结构:

typedef int ElemType;    /* ElemType类型根据实际情况而定,这里假设为int */
/* 线性表的单链表存储结构 */
typedef struct Node
{
     ElemType data;
    struct Node *next;   
} Node;
typedef struct Node *LinkList;

我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称做指针或链。这两部分信息组成数据元素的存储映象,称为结点(Node)。

二叉树:

定义:二叉树(Binary Tree)是n(n >= 0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。

特点:

(1)每个结点最多有两棵子树,所以二叉树中不存在度大于2的结点。(结点拥有的子树数称为结点的度)

(2)左子树和右子树是有顺序的,次序不能任意颠倒。

(3)即使树中某一结点只有一棵子树,也要区分它是左子树还是右子树。

图:

3、系统的总结一遍所有的数据结构知识,即有多少种数据结构,它的基本概念、定义,然后哪几种最常用;

所有的数据结构一共包括:线性表(顺序存储或链式存储,链式存储也称链表)、栈与队列、串、树、图、查找、排序等。其中常用的主要是链表、循环队列、二叉树、排序算法等。有一点要注意,如果是面试做无线通信之类的公司,一定要对二叉树及图的概念要清楚,因为通信类的公司,应用场景往往涉及到多主机之间的通信,而它们之间如何去查找、处理就是最基础的操作了,所以必须对二叉树、图这种常用于通信产品的数据结构要清楚。


三、单片机运行机制

1、中断的处理过程(中断向量表、现场保存);

一般来说,中断的处理分3个过程:中断响应、中断处理、中断返回。

中断响应:

中断响应的过程中需要进行现场保存,也即入栈操作;取向量,即从向量表中找出对应的服务程序入口地址;选择堆栈指针MSP/PSP,更新堆栈指针SP,更新连接寄存器LR,更新程序计数器PC。

在CM3内核中,入栈操作一共包括8个寄存器值的保存,这8个寄存器分别为xPSR、PC、LR、R12及R3-R0,均由硬件自动压入堆栈中。具体入栈顺序如下图所示。

中断处理:

执行相应的处理代码。

中断返回:

中断返回需要将之前保存的现场进行恢复,也即出栈操作,内部的出栈顺序与入栈时的相对应,堆栈指针的值也恢复为先前的值;另外还需要更新NVIC寄存器,因为随着中断的返回,该寄存器中的活动位会被硬件清除。

以上就是中断的大致处理过程,当然,有些会涉及到更为复杂的处理,像中断嵌套、中断延迟等情况,就需要具体问题具体分析了。


四、通讯接口

1、串口通讯的原理及时序;

串行通讯:将数据字节分成一位一位的形式在一条传输线上逐位发送的过程。

异步串行通讯的一般格式是:起始位+数据位+停止位。其中起始位为1位,数据位可以是5、6、7、8位,停止位可以是1、1.5、2位。起始位是一个值为0的位,所以对于正逻辑的TTL电平,起始位是一位时间的低电平;停止位是值为1的位,所以对于正逻辑的TTL电平,停止位是一位时间的高电平。对于负逻辑(如RS-232电平)则相反。

2、IIC通讯的原理及时序;

IIC通讯一共是2条总线:串行数据总线(SDA)、串行时钟总线(SCL);

IIC通讯属于半双工通讯。它的从设备地址一共是7bit,位于地址字节中的高7位,地址字节的最低位bit0指示通讯的方向(0表示写操作,1表示读操作);

IIC总线在进行数据传输时,SLC在高电平期间,SDA上的电平必须保持稳定;SDA上电平的状态只有在SCL电平为低时才能改变。

起始信号和终止信号:

起始信号实质为一组组合信号:SCL保持高电平不变,SDA由高电平变为低电平。终止信号实质为:SCL保持高电平不变,SDA由低电平变为高电平。如下图所示。

应答信号与非应答信号:

应答信号(A):接收数据的IC在接收到8位数据后,向发送数据的IC发出的特定的低电平脉冲。每一个数据字节后面都要 跟一位应答信号,表示已收到数据。应答信号在第9个时钟周期出现,这时发送器必须在这一时钟位上释放数据线,由接收设备拉低SDA电平来产生应答信号,由 接收设备保持SDA的高电平来产生非应答信号(                                               ),如下图所示。

3、SPI通讯的原理及时序;

注意:DS18B20的通讯方式是单总线通讯,该通讯方式由Maxim全资子公司Dallas设计,它仅采用一根信号线,即传输时钟,又传输数据,而且数据传输是双向的。它与SPI通讯完全是两码事,所以不要再把这个和SPI通讯搞混淆了,很丢面子的,毕竟是最基础的概念。

SPI通讯一共是4条线:数据输入线(SDI)、数据输出线(SDO)、时钟线(SCLK)、片选线(CS)。

SPI通讯属于全双工通讯,该通讯标准由Motorola公司推出。SPI通讯有4种不同的工作模式,可通过CPOL(时钟极性)和CPHA(时钟相位)来选择相应的通讯模式。其中,时钟极性CPOL用于配置SCLK电平在哪种状态时为空闲态或有效态,时钟相位CPHA用于配置数据采样是在第几个边沿。具体的模式时序可参考其他详细资料,此处不再赘述。

总结:嵌入式岗位,有很公司都是要求熟悉基本的通讯协议的,像USART、IIC、SPI、以及其他等。所以,如果面试这一类的公司,面试之前至少要复习下这方面的知识点。


五、操作系统

1、任务死锁:

概念:死锁又称抱死(deadlock),指2个任务无限期的互相等待对方控制着的资源。假如任务T1正独享资源R1,任务T2正独享资源R2,而此时T1又要独享R2,T2也要独享R1,于是两个任务都无法继续运行了,这就是死锁


六、逻辑题

1、小明一家过一座桥,过桥时候是黑夜,所以必须有灯(人需要跟灯一起走)。其中小明过桥要1秒,小明的弟弟要3秒,小明的爸爸要6秒,小明的妈妈要8秒,小明的爷爷要12秒。每次过桥最多可过两人,而过桥的速度依过桥最慢者而定,而且灯在点燃后30秒就会熄灭。问小明一家如何过桥?

小明和弟弟过去(3s),小明回来(1s),妈妈和爷爷过去(12s),弟弟回来(3s),小明和爸爸过去(6s),小明回来(1s),小明和弟弟过去(3s)。所以一共耗时为:3+1+12+3+6+1+3 = 29s。

解这种问题主要是思维不要太死板,不要老想着用耗时最少的和最多的来搭配,返回时间最少,而要考虑如何用耗时最长的几个一起过去,在总体上节约时间。

2、有4个装药丸的罐子,其中一个被污染,被污染的罐子重量是正常罐子+1,只称量一次,如何判断哪个罐子被污染?

这个题在我看来,必须要用天平来称量才行。网上有很多说的是每个罐子里面分别拿1、2、3、4颗出来称,就知道哪个被污染了,但关键是,你根本就不知道正常的重量是多少,你怎么去比对。所以必须要用天平来称,才能做到一次性称出来。

使用天平称量,4个罐子分别标号为A、B、C、D,然后从A、B、C、D号罐子里面分别取1、2、3、4颗药丸,放置在天平的左边,然后从任意一个罐子(比如A)中取10颗药丸放置在天平的右边。如果是右边重,则表明A被污染,如果是左边重,则表明A正常,左边重2,则为B,重3,则为C,重4,则为D,如果右边重,则为A。

这个题主要是要考虑从罐子中单独取药丸出来称,而不是把整个罐子放上去称,那样是称不出来的。


七、项目

写在简历上的项目必须非常熟悉,每一个点都要了然,否则就不要写上去,自己都说不清楚,还写在上面,然后别人问起来,你支支吾吾,这完全是自己在给自己找麻烦,很大的降低了面试成功率;

你在项目中碰到了哪些问题,如何解决的?

这个的话可以事先挑选自己认为比较有难度的几个问题,比如上次那个bootloader层没有关systick中断,导致跳转至应用层之后程序跑飞的问题、比如SD卡操作的某些地方缺少互斥处理,导致写SD卡操作异常,并引发USB中断失效,系统看起来像死机一样的问题、比如对ASCII命令解析函数对天宝命令不能准确过滤的问题、比如那个临时静态失锁后导致卫星灯红灯不闪的问题、比如那个设置临时静态闪灯和差分闪灯不一致问题。

总结:写在简历上的东西必须是自己非常熟悉的,否则就不要写上去。关于项目中碰到的问题,主要要讲下问题是如何定位的(什么样的思路,采用什么样的方法,怎样缩小问题范围,如何复现,最后确定问题根源),因为毕竟问题修复可能非常简单,而问题点的定位才是难点。


八、离职原因

无论你面试哪家公司,面试官是肯定会问你这个问题的,离职原因是什么?这个嘛,说一个两个就行了,尽量说真实的原因,如果真实原因的确会不利于面试成功或者是谈薪资,那就可以说下其他的原因,可以说职业规划啊、环境啊、家人啊、企业文化啊,都可以,反正要有一个看起来不算很敷衍的理由。