万格通讯嵌入式工程师笔试题.docx
- 文档编号:2626349
- 上传时间:2023-05-04
- 格式:DOCX
- 页数:28
- 大小:123.26KB
万格通讯嵌入式工程师笔试题.docx
《万格通讯嵌入式工程师笔试题.docx》由会员分享,可在线阅读,更多相关《万格通讯嵌入式工程师笔试题.docx(28页珍藏版)》请在冰点文库上搜索。
万格通讯嵌入式工程师笔试题
嵌入式工程师笔试题
1.用预处理指令定义一天中有多少毫秒?
答:
#defineSECONDS_PER_YEAR(60*60*24)UL
1).#define语法的基本知识(例如:
不能以分号结束,括号的使用,等等)
2).懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一天中有多少秒而不是计算出实际的值,是更清晰而没有代价的。
3).意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。
2.函数的参数传递有哪几种方式?
这几种方式有什么区别?
下面介绍3种函数参数传递方式。
1.将变量名作为形参和实参
在这种情况下传给形参的是变量的值。
传递是单向的,即如果在执行函数期间形参的值发生变化,并不传回给实参,这就是值传递方式。
因为在调用函数期间,形参和实参不是同一个存储单元。
intmain(){
voidswap(int,int);//参数为整型变量
inti=3,j=4;
cout<<"i="<
swap(i,j);//变量名
cout<<"i="<
system("PAUSE");
return0;
}
voidswap(inta,intb){//形参为整型变量
inttemp;
temp=a;
a=b;
b=temp;
}
结果:
i=3,j=4
i=3,j=4
可以发现,执行函数swap后,形参a和b的改变不会影响实参i和j的值。
2.传递变量指针
形参是指针变量,实参是一个变量的地址,调用函数时,形参(指针变量)指向实参变量单元。
intmain(){
voidswap(int*,int*);//参数为整型指针变量
inti=3,j=4;
cout<<"i="<
swap(&i,&j);//变量地址
cout<<"i="<
system("PAUSE");
return0;
}
voidswap(int*p1,int*p2){//形参为整型指针变量
inttemp;
temp=*p1;
*p1=*p2;
*p2=temp;
}
结果:
i=3,j=4
i=4,j=3
调用函数时把变量i和j的地址传送给形参p1和p2,因此*p1和i为同一内存单元,*p2和j是同一内存单元。
这种方式还是“值传递”,只不过实参的值是变量的地址而已。
而在函数中改变的不是实参的值(即地址,这种改变也影响不到实参),而是实参地址所指向的变量的值。
3.“引用形参”
intmain(){
voidswap(int&,int&);//参数为整型变量的引用
inti=3,j=4;
cout<<"i="<
swap(i,j);//变量名
cout<<"i="<
system("PAUSE");
return0;
}
voidswap(int&a,int&b){//形参为引用类型
inttemp;
temp=a;
a=b;
b=temp;
}
结果:
i=3,j=4
i=4,j=3
当main函数调用swap函数时,由实参把变量名传给形参。
i的名字传给引用变量a,j的名字传给引用变量b。
此时a和b就分别与i,j占用同一内存空间。
这种把实参地址传递到形参,使形参的地址取实参的地址,从而使形参与实参共享同一单元的方式,就是地址传递方式。
这里要说明的是,
[1]方式2传递指针变量要另外开辟内存单元,其内容为地址;而方式3引用不是一个独立的变量,不单独占内存单元。
[2]方式3中,main函数调用swap函数时,实参不必用函数的地址(即&i,&j),而直接使用变量名。
系统向形参传递的是实参的地址而不是实参的值。
3.用变量temp给出下面的定义:
a)一个整型数
b)一个指向整型数的指针
c)一个指向指针的指针,它指向的指针是指向一个整型数
d)一个有10个整型数的数组
e)一个有10个指针的数组,该指针是指向一个整型数
e)一个指向有10个整型数数组的指针
g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数
h)一个有10个指针的数组,该指针指向一个函数,该函数有一个一个整型参数并返回一个整型数
答:
a)inttemp;
b)int*temp;
c)int**temp;
d)inttemp[10];
e)int*temp[10];
f)int(*temp)[10];
g)int(*temp)(int);
h)int(*temp[10])(int);
4.关键字static的作用是什么?
答:
在C语言中,关键字static有三个明显的作用:
1).在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2).在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。
它是一个本地的全局变量。
3).在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。
那就是,这个函数被限制在声明它的模块的本地范围内使用。
5关键字const是什么含义?
答:
对于“const意味着常数”这个答案是业余者给出的
比较正确的答案是“只读”
1) 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。
如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。
(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。
)
2) 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
3) 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。
简而言之,这样可以减少bug的出现。
const关键字至少有下列几个作用:
(1)欲阻止一个变量被改变,可以使用const关键字。
在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。
例如:
constclassAoperator*(constclassA&a1,constclassA&a2);
operator*的返回结果必须是一个const对象。
如果不是,这样的变态代码也不会编译出错:
classAa,b,c;
(a*b)=c;// 对a*b的结果赋值
操作(a*b)=c显然不符合编程者的初衷,也没有任何意义。
6.do……while和while……do有什么区别?
答:
前一个循环一遍再判断,后一个判断以后再循环。
7.局部变量能否和全局变量重名?
答:
能,局部会屏蔽全局。
要用全局变量,需要使用":
:
"局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。
对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内
8.如何引用一个已经定义过的全局变量?
答:
extern可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错
9.char*consttemp_ptr;charconst*temp_ptr;constchar*temp_ptr;
上述三个temp_ptr分别代表什么含义和有什么区别?
答:
char*constp;//常量指针,p的值不可以修改
charconst*p;//指向常量的指针,指向的常量值不可以改constchar*p;//和charconst*p
constchartemp_ptr;
charconsttemp_ptr;
上面两个temp_ptr是一个常字符。
23-----constchar*temp_ptr;
上面的temp_ptr是一个指向常字符的指针(也就是,指向的字符是不可修改的,但指针可以,此最常见于函数的参数,当你只引用传进来指针所指向的值时应该加上const修饰符,程序中修改编译就不通过,可以减少程序的bug)。
Aconstchar*temp_ptr;或charconst*temp_ptr;//*temp_ptr是const,但指针temp_ptr可变
Bconst*chara;或char*consta;//a是const,但*a可变
C(constchar*consta;等价于char*constaconst;)或charconst*consta;//a和*a都是const,常量和指针的值都不能改变
1------char*consttemp_ptr;
上面的temp_ptr是一个指向字符的常指针(也就是说,指针指向的字符是可以修改的,但指针是不可修改的)。
charconst*temp_ptrconst;
上面的temp_ptr是一个指向常字符的常指针(也就是说,指针指向的字符是不可修改的,同时指针也是不可修改的)。
本质:
const在谁后面谁就不可修改,const在最前面则将其后移一位即可,二者等效
10.请问以下代码有什么问题
intmain()
{
chara;
char*temp_str=&a;
strcpy(temp_str,”hello”);
printf(temp_str);
return0;
}
答:
没有为str分配内存空间,将会发生异常
问题出在将一个字符串复制进一个字符变量指针所指地址。
虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。
11.请写出下列代码的输出内容
#include
intmain()
{
inta,b,c,d;
a=10;
b=a++
c=++a;
d=10*a++;
printf(“b,c,d:
%d,%d,%d\n”,b,c,d);
return0;
}
答、10,12,120
12.inta=5,b=7,c;
c=a+++b;
a,b,c的值是多少?
答:
完全合乎语法的。
问题是编译器如何处理它?
根据最处理原则,编译器应当能处理尽可能所有合法的用法。
因此,上面的代码被处理成:
c=a+++b;
因此,这段代码持行后a=6,b=7,c=12。
13.下面的代码输出是什么,为什么?
voidfoo(void)
{
unsignedinta=6;
intb=-20;
(a+b>6)?
puts(“>6”):
puts(“<=6);
}
答:
这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。
不管如何,这无符号整型问题的答案是输出是">6"。
原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。
因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。
这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。
14.对于一个频繁使用的短小函数,在C语言中应用什么实现,在C++中应用什么实现?
答:
c用宏定义,c++用inline
15.假设有如下定义:
#definedPSstructs*
typedefstructs*tPS;
那么
dPSp1,p2;和tPSp1,p2;是否一样?
答:
第一个扩展为
structs*p1,p2;
上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。
第二个例子正确地定义了p3和p4两个指针。
16.对于整型变量A=0x12345678,请画出在littleendian及bigendian的存储方式下的,在内存中是如何存储的。
答:
高
高
78
12
Little:
Big:
56
43
34
56
12
78
低
低
17.引用与指针有什么区别?
答:
指针和引用有什么分别;如果传引用比传指针安全,为什么?
如果我使用常量指针难道不行吗?
答案:
引用必须初始化,指针不用;引用初始化后不能被改变,而指针可以改变所指向的对象;不存在指向空值的引用,但是存在可以指向空值的指针。
解释:
(1)引用在创建的同时必须初始化,即引用到一个有效的对象;而指针在定义的时候不必初始化,可以在定义后面的任何地方重新赋值.
(2)不存在NULL引用,引用必须与合法的存储单元关联;而指针则可以是NULL.
(3)引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用;而指针在任何时候都可以改变为指向另一个对象.给引用赋值并不是改变它和原始对象的绑定关系.
(4)引用的创建和销毁并不会调用类的拷贝构造函数
(5)语言层面,引用的用法和对象一样;在二进制层面,引用一般都是通过指针来实现的,只不过编译器帮我们完成了转换.
不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,显得很安全。
const指针仍然存在空指针,并且有可能产生野指针.
总的来说:
引用既具有指针的效率,又具有变量使用的方便性和直观性.
18.什么是平衡二叉树?
答:
且具有以下性质:
它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
构造与调整方法平衡二叉树的常用算法有红黑树、AVL、Treap、伸展树等。
最小二叉平衡树的节点的公式如下F(n)=F(n-1)+F(n-2)+1这个类似于一个递归的数列,可以参考Fibonacci数列1是根节点F(n-1)是左子树的节点数量F(n-2)是右子数的节点数量。
左右子树都是平衡二叉树且左右子树的深度差值的绝对值不大于1。
19.数组和链表的区别?
答:
数组:
数据顺序存储,固定大小
链表:
数据可以随机存储,大小可动态改变
20.队列和栈有什么区别?
答:
1.队列先进先出,栈先进后出。
2.对插入和删除操作的"限定"。
栈是限定只能在表的一端进行插入和删除操作的线性表。
队列是限定只能在表的一端进行插入和在另一端进行删除操作的线性表。
从"数据结构"的角度看,它们都是线性结构,即数据元素之间的关系相同。
但它们是完全不同的数据类型。
除了它们各自的基本操作集不同外,主要区别是对插入和删除操作的"限定"。
栈和队列是在程序设计中被广泛使用的两种线性数据结构,它们的特点在于基本操作的特殊性,栈必须按"后进先出"的规则进行操作,而队列必须按"先进先出"的规则进行操作。
和线性表相比,它们的插入和删除操作受更多的约束和限定,故又称为限定性的线性表结构。
3.遍历数据速度不同。
栈只能从头部取数据也就最先放入的需要遍历整个栈最后才能取出来,而且在遍历数据的时候还得为数据开辟临时空间,保持数据在遍历前的一致性队列怎不同,他基于地址指针进行遍历,而且可以从头或尾部开始遍历,但不能同时遍历,无需开辟临时空间,因为在遍历的过程中不影像数据结构,速度要快的多
栈(Stack)是限定只能在表的一端进行插入和删除操作的线性表。
队列(Queue)是限定只能在表的一端进行插入和在另一端进行删除操作的线性表。
从"数据结构"的角度看,它们都是线性结构,即数据元素之间的关系相同。
但它们是完全不同的数据类型。
除了它们各自的基本操作集不同外,主要区别是对插入和删除操作的"限定"。
栈和队列是在程序设计中被广泛使用的两种线性数据结构,它们的特点在于基本操作的特殊性,栈必须按"后进先出"的规则进行操作,而队列必须按"先进先出"的规则进行操作。
和线性表相比,它们的插入和删除操作受更多的约束和限定,故又称为限定性的线性表结构。
可将线性表和栈及队列的插入和删除操作对比如下:
线性表
Insert(L,i,x)
(1≤i≤n+1)
Delete(L,i)
(1≤i≤n)
如线性表允许在表内任一位置进行插入和删除
栈
Insert(L,n+1,x)
Delete(L,n)
而栈只允许在表尾一端进行插入和删除
队列
Insert(L,n+1,x)
Delete(L,1)
队列只允许在表尾一端进行插入,在表头一端进行删除
21.一个32位的机器,该机器的指针是多少位?
答:
指针是多少位只要看地址总线的位数就行了。
80386以后的机子都是32的数据总线。
所以指针的位数就是4个字节了。
22.操作系统中进程调度策略有哪几种?
答:
FCFS(先来先服务),优先级,时间片轮转,多级反馈
23.进程之间通信的途径?
答:
共享存储系统
消息传递系统
管道:
以文件系统为基础
24.列举几种进程的同步机制,并比较其有缺点?
答:
1.信号量机制:
一个信号量只能置一次初值,以后只能对之进行p操作或v操作。
由此也可以看到,信号量机制必须有公共内存,不能用于分布式操作系统,这是它最大的弱点。
2.自旋锁:
旋锁是为了保护共享资源提出的一种锁机制。
调用者申请的资源如果被占用,即自旋锁被已经被别的执行单元保持,则调用者一直循环在那里看是否该自旋锁的保持着已经释放了锁
自旋锁是一种比较低级的保护数据结构和代码片段的原始方式,可能会引起以下两个问题;
(1)死锁
(2)过多地占用CPU资源
3.管程:
信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难。
因此后来又提出了一种集中式同步进程——管程。
其基本思想是将共享变量和对它们的操作集中在一个模块中,操作系统或并发程序就由这样的模块构成。
这样模块之间联系清晰,便于维护和修改,易于保证正确性。
4.会合:
进程直接进行相互作用
5.分布式系统:
由于在分布式操作系统中没有公共内存,因此参数全为值参,
而且不可为指针。
25.进程死锁的原因?
答:
资源竞争及进程推进顺序非法
26.线程休眠以后对主进程有何影响?
答:
进程状态:
S:
sleep睡眠状态
O:
可运行状态
T:
挂起状态(阻塞)
R:
运行状态
Z:
僵尸(僵死)进程
进程之间的父子关系:
1)A进程启动了B进程,A就是B的父进程。
启动之后父子进程同时运行。
如果子进程B先结束,子进程会通知父进程A,父进程会回收子进程的相关资源。
2)父进程启动子进程后同时运行,父进程先结束运行,子进程成为孤儿进程,子进程马上认init为父进程。
进程1(init)一般认为孤儿院。
3)父子进程同时运行,子进程先结束,通知父进程,父进程未响应,子进程结束了。
父进程的进程表项中依然记录着一个子进程,这个子进程就成了僵尸进程。
线程和进程的区别:
1)线程是进程一部分,属于进程
2)进程是资源分配的基本单位,拥有自己独立的内存空间。
3)线程没有自己独立的内存空间,进程中的多个线程共享共享的所有资源(内存...)
4)启动一个进程需要消耗更多的系统资源和系统时间,而启动一个线程需要的资源和时间较少。
5)进程是重量级的,线程是轻量级的。
27.有如下代码,请说明该段代码的作用,然后请评论一下这段代码。
__interruptdoublecompute_area(doubleradius)
{
doublearea=PI*radius*radius;
printf(“Area=%f“,area);
Returnarea;
}
答:
中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。
具代表事实是,产生了一个新的关键字__interrupt。
下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR)
这个函数的具体错误有:
1).ISR不能返回一个值。
2).ISR不能传递参数。
3).在许多的处理器/编译器中,浮点一般都是不可重入的。
有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。
此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。
4).与第三点一脉相承,printf()经常有重入和性能上的问题。
28.中断(如键盘中断)与异常(如除零异常)有何区别?
中断通常被分为“同步中断”和异步中断两大类。
同步中断是指当指令执行时由CPU控制单元产生的中断,之所以称为“同步中断”是因为只有在一条指令中止执行后CPU才会发出这类中断信号。
而异步中断(通常意义上的中断,来自外部)则是指由其他硬件设备依照CPU时钟随机产生的中断信号。
在Intel80x86CPU手册中,同步中断和异步中断也被分别称为“异常(Exception)”和“中断(Interrupt)”。
一、中断:
系统停止当前正在运行的程序而转向其他服务,可能是因为优先级高的请求
服务了,或者是因为人为安排中断。
中断是属于正常现象。
异常:
是由于软件错误而引起的
二、中断是CPU所具备的功能 -- 硬件
异常是软件运行过程中的一种开发过程中没有考虑到的程序错误 -- 软件
三、
1.中断的概念
所谓中断是指CPU对系统发生的某个事件作出的一种反应:
CPU暂停正在执行的程序,
保留现场后自动地转去执行相应的处理程序,处理完该事件后再返回断点继续执行被“
打断”的程序。
引起中断的事件称为中断源,中断源向CPU提出进行处理的请求称为中断请求。
2.中断类型
按中断事件来源进行分类,主要有两类:
(1)中断。
由CPU以外的事件引起的中断,如I/O中断、时钟中断、控制台中断等。
(2)异常(exception)。
来自CPU的内部事件或程序执行中的事件引起的过程。
如
由于CPU本身故障、程序故障和请求系统服务的指令引起的中断等。
3.中断的一般处理过程
中断处理一般分为中断响应和
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 通讯 嵌入式 工程师 笔试