说在前面的话:
由于自己的本科是双非院校,所以自己在考研过程中也参加了找工作的前列,一方面是怕自己如果没考上怎么办,找个工作还可以保险一点;另一方面就是怕初试过了,面试因为自己是双非的而被pass掉。相必大家大家会问,你准备考研还有时间去找工作???确实!!!我当时也是也是非常担心纠结,要不要去让费时间去找工作,了解同学们找工作的经历以及自己的再三纠结,还是去尝试了一下,于是开始在找一些面试题以及技巧什么的,就感觉像临时抱佛脚。(在这声明一下:还是不建议大家在准备考研期间去找工作,这样会让你分心,而且很严重,我当时是花费了一周蔡慢慢缓过来重新进入考研状态)。非常幸运的是通过以下的面试题我居然面试过了,收到offer之后,我又重新进入考研的状态。可能是因为找到了工作也没有后顾之忧,反正考得上越好,考不上就去工作,以轻松的心态准备考研,结果是居然考上了。后来经过选择我还是去读研了!!!!!下面分享一下我当时看过的一些面试题,虽然老,但都是精华!!!为了整理这些花费了我两天的时间。。。哇。。。
因为疫情的原因,现在就业形势非常严峻。现在嵌入式发展这么快,很多人开始踏上嵌入式学习之路,据市场统计,一般毕业或者找工作的一些人,在面试公司的时候,都会在这个地方卡壳,那就是面试题,很多人都是面试的很好,但是在做面试题的时候,手下的功夫不够深,最后被面试题而刷了下来,使得找工作变成了难题,空有一身的才华了。以下是对一些嵌入式面试的一些空难户而总结,可以学学哦,去公司面试一般都是会有的。
(1)
char * constp char const * p const char *p
上述三个有什么区别?
答案:
char * const p; //常量指针,p的值不可以修改
char const * p;//指向常量的指针,指向的常量值不可以改 const char *p; //和char const *p
char * const p;//是一个char型指针,指针是常量,不允许修改,即类似p=q的语句是错误的;
char const * p和const char * p是同一种char型指针,指针指向的字符被看作是常量,不允许修改,即类似*p = q的语句是错误的。
(2)
char str1[] = "abc";
charstr2[] = "abc";
const char str3[] = "abc";
const charstr4[] = "abc";
const char *str5 = "abc";
const char *str6= "abc";
char *str7 = "abc";
char *str8 = "abc";
cout < <( str1 == str2 ) < < endl;
cout < < ( str3 == str4 )< < endl;
cout << ( str5 == str6 ) < < endl;
cout < <( str7 == str8 ) < < endl;
结果是:
0 0 1 1str1,str2,str3,str4
是数组变量,它们有各自的内存空间;
而str5,str6,str7,str8
是指针,它们指向相同的常量区域。
(3)什么是预编译,何时需要预编译:
答案:
1、总是使用不经常改动的大型代码体。
2、程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。
预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。
(4)以下代码中的两个sizeof用法有问题吗?
void UpperCase( char str[] )
{
for( size_t i=0; i<sizeof(str)/sizeof(str[0]); ++i )
if( 'a'<=str[i] && str[i]<='z' )
str[i] -= ('a'-'A' );
}
char str[] = "aBcDe";
cout << "str字符长度为: " << sizeof(str)/sizeof(str[0]) << endl;
UpperCase( str );
cout << str << endl;
答案:
函数内的sizeof有问题。根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。函数外的str是一个静态定义的数组,因此其大小为6,函数内的str实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,因此sizeof作用于上只将其当指针看,一个指针为4个字节,因此返回4。
(5)嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?
这个问题用几个解决方案。我首选的方案是:
while(1)
{
}
一些程序员更喜欢如下方案:
for(;;)
{
}
这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的基本原理。如果他们的基本答案是:“我被教着这样做,但从没有想到过为什么。”这会给我留下一个坏印象。
第三个方案是用
goto
Loop:
...
goto Loop;
应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。
const
(6)关键字
const
是什么含意?
我只要一听到被面试者说:“const意味着常数
”,我就知道我正在和一个业余者打交道。
去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着“只读”就可以了
。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。)
如果应试者能正确回答这个问题,我将问他一个附加的问题:下面的声明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。
顺带提一句,也许你可能会问,即使不用关键字const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由:
1). 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)
2). 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
3). 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。
volatile
(7)关键字
volatile
有什么含意 并给出三个不同的例子。
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。
假设被面试者正确地回答了这是问题(嗯,怀疑这是否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。
1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数有什么错误:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。
它是const因为程序不应该试图去修改它。
2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer
的指针时。
3). 这段代码的有个恶作剧。这段代码的目的是用来返指针ptr指向值的平方,但是,由于ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
位操作(Bit manipulation)
(8)嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit
3。在以上两个操作中,要保持其它位不变。
对这个问题有三种基本的反应:
1). 不知道如何下手。该被面者从没做过任何嵌入式系统的工作。
2). 用bit fields
。Bit fields是被扔到C语言死角的东西,它保证你的代码在不同编译器之间是不可移植的,同时也保证了的你的代码是不可重用的。我最近不幸看到Infineon为其较复杂的通信芯片写的驱动程序,它用到了bit fields因此完全对我无用,因为我的编译器用其它的方式来实现bit fields的。
从道德讲:永远不要让一个非嵌入式的家伙粘实际硬件的边。
3). 用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法,是应该被用到的方法。最佳的解决方案如下:
#define BIT3 (0x1 < <3)
static int a;
void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}
一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的。
我希望看到几个要点:说明常数、|=和&=~操作
。
数据声明(Data declarations)
(9)用变量a给出下面的定义
a) 一个整型数(
An integer
)
b) 一个指向整型数的指针(A pointer to an integer
)
c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer
)
d) 一个有10个整型数的数组(An array of 10 integers
)
e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers
)
f) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers
)
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer
)
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数(An array of ten pointers to functions that take an integer argument and return an integer
)
答案是:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
人们经常声称这里有几个问题是那种要翻一下书才能回答的问题,我同意这种说法。当我写这篇文章时,为了确定语法的正确性,我的确查了一下书。
但是当我被面试的时候,我期望被问到这个问题(或者相近的问题)。因为在被面试的这段时间里,我确定我知道这个问题的答案。应试者如果不知道所有的答案(或至少大部分答案),那么也就没有为这次面试做准备,如果该面试者没有为这次面试做准备,那么他又能为什么出准备呢?
Static
(10) 关键字static的作用是什么?
这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:
1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。
(11)一个32位的机器,该机器的指针是多少位答案:
指针是多少位只要看地址总线的位数就行了。80386以后的机子都是32的数据总线。所以指针的位数就是4个字节了。
(12) 以下代码输出的是什么,为什么??
main()
{
inta[5]={1,2,3,4,5};
int *ptr=(int*)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
}
答案:2。
5 *(a+1)
就是a[1]
,*(ptr-1)
就是a[4]
,执行结果是2
,5 &a+1
不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int) int *ptr=(int *)(&a+1);
则ptr
实际是&(a[5])
,也就是a+5
原因如下: &a
是数组指针,其类型为 int (*)[5]
; 而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同 。a
是长度为5的int
数组指针,所以要加5*sizeof(int)
所以ptr
实际是a[5]
但是prt
与(&a+1)
类型是不一样的(这点很重要) 所以prt-1
只会减去sizeof(int*) a
,&a
的地址是一样的,但意思不一样,a
是数组首地址,也就是a[0]
的地址,&a
是对象(数组)首地址,a+1
是数组下一元素的地址,即a[1]
,&a+1
是下一个对象的地址,即a[5]
.
(13)请问以下代码有什么问题:
int main()
{
char a;
char *str=&a;
strcpy(str,"hello");
printf(str);
return 0;
}
答案:
没有为str分配内存空间,将会发生异常问题出在将一个字符串复制进一个字符变量指针所指地址。虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。
(14)
char* s="AAA"; printf("%s",s); s[0]='B'; printf("%s",s);
有什么错?
答案:
"AAA"
是字符串常量。s
是指针,指向这个字符串常量,所以声明s的时候就有问题。 cosnt char*s="AAA";
然后又因为是常量,所以对是s[0]
的赋值操作是不合法的。
(15)写一个“标准”宏,这个宏输入两个参数并返回较小的一个。
答案:.#defineMin(X, Y) ((X)>(Y)?(Y):(X)) //结尾没有‘;’
(16)嵌入式系统中经常要用到无限循环,你怎么用C编写死循环。
答案:while(1){}或者for(;;)
(17)关键字static的作用是什么?
答案:定义静态变量
(18)关键字const有什么含意?
答案:表示常量不可以修改的变量。
(19)关键字volatile有什么含意?并举出三个不同的例子?
答案:提示编译器对象的值可能在编译器未监测到的情况下改变。
(20)
int (*s[10])(int)
表示的是什么啊?
答案:int(*s[10])(int)
函数指针数组,每个指针指向一个int func(int param)
的函数。
(21)有以下表达式:
int a=248;
b=4;
int const c=21;
const int *d=&a;
int *conste=&b;
int const *f const =&a;
请问下列表达式哪些会被编译器禁止?为什么?
答案:
*c=32;d=&b;*d=43;e=34;e=&a;f=0x321f;
*c 这是个什么东东,禁止 ;*d 说了是const, 禁止; e = &a 说了是const ,禁止; const *fconst =&a 禁止。
(22)交换两个变量的值,不使用第三个变量。即a=3,b=5,交换之后a=5,b=3;
答案:
有两种解法, 一种用算术算法, 一种用^(异或)
a = a + b;
b = a - b;
a = a - b;
or
a = a^b;// 只能对int,char..
b = a^b;
a = a^b;
or
a^= b ^= a;
(23)c和c++中的struct有什么不同?
答案:
c和c++中struct的主要区别是c中的struct不可以含有成员函数,而c++中的struct可以。c++中struct和class的主要区别在于默认的存取权限不同,struct默认为public,而class默认为private
(24)
void getmemory(char *p)
{
p=(char *)malloc(100);
strcpy(p,"helloworld");
}
int main( )
{
char*str=NULL;
getmemory(str);
printf("%s/n",str);
free(str);
return 0;
}
答案:
程序崩溃,getmemory中的malloc 不能返回动态内存, free()对str操作很危险
(25)
charszstr[10]; strcpy(szstr,"0123456789");
产生什么结果?为什么?
答案: 长度不一样,会造成非法的OS
(26)列举几种进程的同步机制,并比较其优缺点。
答案: 原子操作 信号量机制 自旋锁 管程,会合,分布式系统
(27)进程之间通信的途径
答案:共享存储系统消息传递系统管道:以文件系统为基础
(28)进程死锁的原因
答案:资源竞争及进程推进顺序非法
(29)死锁的4个必要条件
答案:互斥、请求保持、不可剥夺、环路
(30)死锁的处理
答案:鸵鸟策略、预防策略、避免策略、检测与解除死锁
(31)操作系统中进程调度策略有哪几种?
答案:FCFS(先来先服务),优先级,时间片轮转,多级反馈
(32)类的静态成员和非静态成员有何区别?
答案:类的静态成员每个类只有一个,非静态成员每个对象一个
(33)纯虚函数如何定义?使用时应注意什么?
答案:virtualvoid f()=0; 是接口,子类必须要实现
(24)数组和链表的区别
答案:数组:数据顺序存储,固定大小连表:数据可以随机存储,大小可动态改变
这些都是你在面试中,会遇到的一些面试问题,赶紧好好积累起来,那样对你以后的嵌入式学习或者嵌入式面试都会是巨大的财富,嵌入式题库这里没有最好,只有更好,好好掌握这些嵌入式题库,那对于嵌入式来说面试so easy。
**备注:
各位老铁们决定可以的话来个三连击,鼓励鼓励以下这只大白各位老铁们决定可以的话来个三连击,鼓励鼓励以下这只大白
各位老铁们决定可以的话来个三连击,鼓励鼓励以下这只大白**