一、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命令解析函数对天宝命令不能准确过滤的问题、比如那个临时静态失锁后导致卫星灯红灯不闪的问题、比如那个设置临时静态闪灯和差分闪灯不一致问题。
总结:写在简历上的东西必须是自己非常熟悉的,否则就不要写上去。关于项目中碰到的问题,主要要讲下问题是如何定位的(什么样的思路,采用什么样的方法,怎样缩小问题范围,如何复现,最后确定问题根源),因为毕竟问题修复可能非常简单,而问题点的定位才是难点。
八、离职原因
无论你面试哪家公司,面试官是肯定会问你这个问题的,离职原因是什么?这个嘛,说一个两个就行了,尽量说真实的原因,如果真实原因的确会不利于面试成功或者是谈薪资,那就可以说下其他的原因,可以说职业规划啊、环境啊、家人啊、企业文化啊,都可以,反正要有一个看起来不算很敷衍的理由。