笔试经典题.docx
《笔试经典题.docx》由会员分享,可在线阅读,更多相关《笔试经典题.docx(23页珍藏版)》请在冰点文库上搜索。
笔试经典题
1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)。
#defineSECONDS_PER_YEAR(365*24*60*60)UL
2 . 写一个"标准"宏MIN ,这个宏输入两个参数并返回较小的一个。
#defineMIN((A)<=(B)?
(A):
(B))
3. 预处理器标识#error的目的是什么?
#error记号序列
将使预处理器打印包含该记号序列的诊断信息。
4. 嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?
While
(1)
{
}
5. 用变量a给出下面的定义
a) 一个整型数(An integer)
b)一个指向整型数的指针( A pointer to an integer)
c)一个指向指针的的指针,它指向的指针是指向一个整型数( A pointer to a pointer to an intege)r
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)inta;
b)int*a;
c)int**a;
d)inta[10];
e)int*a[10];
f)int(*a)[10];
g)int(*a)(int);
h)int(*a[10])(int);
6. 关键字static的作用是什么?
答:
1)在函数体内,被声明为静态的变量在函数内被调用的过程中维持值不变,这个局部变量存储域发生改变,从栈到data中,所以这个值不变。
(static局部变量只被初始化一次,下一次依据上一次结果值;
2)在模块内被声明的静态全局变量,能被模块内部声明之后的函数调用,但不能被模块外的函数调用。
这是一个本地的变量。
所以声明为静态的全局变量的作用域发生改变,限制了它的使用范围。
3)在模块内的声明的静态函数,可以被模块内部的其他函数调用,但不可被模块外部的函数调用。
也就是这个函数被限制在声明它的模块使用。
对于可在原模块外部使用的函数,应该在一个头文件中说明,要使用这些函数的模块需包含这个头文件。
7.关键字const有什么含意?
1)使用Const关键字的地方是为了说明这个参数为常量,是不应该被修改的。
2)合理使用const可以是编译器自然的保护那些不希望被修改的参数,防止被无意的代码修改。
3)通过给优化器一些有用的信息,使用关键字const也许是代码更加紧凑。
题:
1.有以下表达式:
inta=248;b=4;
intconstc=21;
constint*d=&a;
int*conste=&b;
intconst*fconst=&a;
请问下列表达式哪些会被编译器禁止?
为什么?
*c=32;
d=&b;
*d=43;
e=34;e=&a;
f=0x321f;
*c这是个什么东东,禁止
*d说了是const,禁止
e=&a说了是const禁止
const*fconst=&a;禁止
下面的声明都是什么意思?
1)const int a;
2)int const a;
3)const int *a;
4)int * const a;
5)int const * a const;
1)a是一个常整数;
2)a是一个常整数;
3)a是一个指向常整形的指针
4)a是一个指向整型的常指针;
5)a是一个指向常整型的常指针;
8. 关键字volatile有什么含意?
并给出三个不同的例子。
一个定义为volatile的变量就是说它可能会意想不到的改变(改变它的情况有很多,例如操作系统,硬件,线程),这样,编译器就不会去假设这个值,也就是说,当优化器读到这个值时就会小心翼翼的去重新读取这个变量的值,而不是使用保存在寄存器中的备份。
(当使用到用类型修饰符声明的变量时,系统总是从它所在的内存读取,既使系统刚从这里读取过)
Volatile修饰符告诉编译程序不要对该变量所参与的操作进行优化。
大多数计算机拥有一系列寄存器,其存取速度比计算机主存更快。
好的编译程序能进行一种被称为“冗余装入和存储的删去”(redundantloadandstoreremoval)的优化,即编译程序会在程序中寻找并删去这样两类代码:
一类是可以删去的从内存装入数据的指令,因为相应的数据已经被存放在寄存器中;另一种是可以删去的将数据存入内存的指令,因为相应的数据在再次被改变之前可以一直保留在寄存器中。
例如:
volatilei=10;inta=i;
例子:
1)并行设备的硬件寄存器。
存储器映射的硬件寄存器通常加volatile
例如状态寄存器。
以为设备寄存器会在你的程序不知道或者不介入的时候发生改变,那是因为设备寄存器可以被外设硬件修改。
相反,变量中的不会变。
设备寄存器的内容是易失的,或者在不注意的时候被修改。
当声明指向设备寄存器的指针时一定要用volatile它会告诉编译器不要对存储在这个地址的数据进行假设,编译器在优化这个变量时应该把它看作编译时未知的。
#defineIOPMOD(*((Volatileunsigned*)(SYSCFG+0x5000)))
2)一个中断服务程序中修改的供其他程序检测的变量。
3)多线程应用中被几个任务共享的变量。
1.一个变量可以既是const还是volatile吗?
解释为什么?
可以,例如状态寄存器。
它首先应该是volatile因为它可以出乎意料的修改,而且应该是const,这个寄存器程序不应该去修改。
2.一个指针可以说volatile的吗?
可以,例如中断服务子程序中修改一个指向buffer的指针。
3.下列代码有错误吗?
Intsquare(volatileint*ptr)
{
Return*ptr**ptr;
}
答:
由于ptr声明为volatile类型的所以编译器可能编译完的程序是:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
则得出的结果可能不是想要的结果;
正确的程序这样写:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
9. 嵌入式系统总是要用户对变量或寄存器进行位操作。
给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。
在以上两个操作中,要保持其它位不变。
#defineBIT3(0x01<<3)
Staticinta;
Voidset_bit3(void)
{
a|=BIT3;
}
Voidclear_bit3(void)
{
a&=~BIT3;
}
10. 嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。
在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。
编译器是一个纯粹的ANSI编译器。
写代码去完成这一任务。
这一题目是测试你是否知道为了访问一绝对地址而把一个整型数强制转换成一个指针。
int*ptr;
ptr=(int*)0x67a9;
*ptr=0xaa66;
或者:
*(int*const)(0x67a9)=0xaa66;
11. 中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。
具代表事实是,产生了一个新的关键字 __interrupt。
下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。
__interruptdoublecompute_area(doubleradius)
{
Doublearea=pi*radius*radius;
Printf(“\narea=%f”,area);
Returnarea;
}
1)ISR是不能有返回值。
2)ISR不能传递参数。
3)在许多的处理器/编译器中,浮点一般都是不可重入的。
ISR一般是短而有效的,在ISR中做浮点运算时不明智的。
4)Printf()函数有重入和性能上的问题。
12 . 下面的代码输出是什么,为什么?
Voidfoo(void)
{
Unsignedinta=6;
Intb=-20;
(a+b>6)?
(“>6):
(“<6);
}
答案是“》6”原因是当操作数中有符号类型和无符号类型进行运算时所有的操作数都自动转换成无符号数。
13. 评价下面的代码片断:
Unsignedintzero=0;
Unsignedintcompzero=0xFFFF;
对于int不是16位的处理器来说此代码不正确。
应该写成:
Unsignedintcompzero=~0;
这一问题真正能揭露出应试者是否懂得处理器字长的重要性。
在我的经验里,好的嵌入式程序员非常准确地明白硬件的细节和它的局限,
14. 尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(heap)中动态分配内存的过程的。
那么嵌入式系统中,动态分配内存可能发生的问题是什么?
下面的代码片段的输出是什么,为什么?
Char*ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");
15 Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。
也可以用预处理器做类似的事。
例如,思考一下下面的例子:
15 Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。
也可以用预处理器做类似的事。
例如,思考一下下面的例子:
#define dPS struct s *
typedef struct s * tPS;
以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。
哪种方法更好呢?
(如果有的话)为什么?
这是一个非常微妙的问题,任何人答对这个问题(正当的原因)是应当被恭喜的。
答案是:
typedef更好。
思考下面的例子:
dPS p1,p2;
tPS p3,p4;
第一个扩展为
struct s * p1, p2;
.
上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。
第二个例子正确地定义了p3 和p4 两个指针。
16 . C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么?
int a = 5, b = 7, c;
c = a+++b;
这个问题将做为这个测验的一个愉快的结尾。
不管你相不相信,上面的例子是完全合乎语法的。
问题是编译器如何处理它?
水平不高的编译作者实际上会争论这个问题,根据最处理原则,编译器应当能处理尽可能所有合法的用法。
因此,上面的代码被处理成:
c = a++ + b;
因此, 这段代码持行后a = 6, b = 7, c = 12。
如果你知道答案,或猜出正确答案,做得好。
如果你不知道答案,我也不把这个当作问题。
我发现这个问题的最大好处是这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题。
试题1:
分别给出BOOL,int,float,指针变量与“零值”比较的if语句(假设变量名为var)
解答:
BOOL型变量:
if(!
var)
int型变量:
if(var==0)
float型变量:
constfloatEPSINON=0.00001;
if((x>=-EPSINON)&&(x<=EPSINON)
指针变量:
if(var==NULL)
剖析:
考查对0值判断的“内功”,BOOL型变量的0判断完全可以写成if(var==0),而int型变量也可以写成if(!
var),指针变量的判断也可以写成if(!
var),上述写法虽然程序都能正确运行,但是未能清晰地表达程序的意思。
一般的,如果想让if判断一个变量的“真”、“假”,应直接使用if(var)、if(!
var),表明其为“逻辑”判断;如果用if判断一个数值型变量(short、int、long等),应该用if(var==0),表明是与0进行“数值”上的比较;而判断指针则适宜用if(var==NULL),这是一种很好的编程习惯。
浮点型变量并不精确,所以不可将float变量用“==”或“!
=”与数字比较,应该设法转化成“>=”或“<=”形式。
如果写成if(x==0.0),则判为错,得0分。
试题4:
为什么标准头文件都有类似以下的结构?
#ifndef__INCvxWorksh
#define__INCvxWorksh
#ifdef__cplusplus
extern"C"{
#endif
/*...*/
#ifdef__cplusplus
}
#endif
#endif/*__INCvxWorksh*/
解答:
头文件中的编译宏
#ifndef __INCvxWorksh
#define __INCvxWorksh
#endif
的作用是防止被重复引用。
作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。
函数被C++编译后在symbol库中的名字与C语言的不同。
例如,假设某个函数的原型为:
voidfoo(intx,inty);
该函数被C编译器编译后在symbol库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。
_foo_int_int这样的名字包含了函数名和函数参数数量及类型信息,C++就是考这种机制来实现函数重载的。
为了实现C和C++的混合编程,C++提供了C连接交换指定符号extern"C"来解决名字匹配问题,函数声明前加上extern"C"后,则编译器就会按照C语言的方式将该函数编译为_foo,这样C语言中就可以调用C++的函数了。
试题1:
请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1
解答:
intcheckCPU()
{
{
unionw
{
inta;
charb;
}c;
c.a=1;
return(c.b==1);
}
}
剖析:
嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。
采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。
例如,16bit宽的数0x1234在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址
存放内容
0x4000
0x34
0x4001
0x12
而在Big-endian模式CPU内存中的存放方式则为:
内存地址
存放内容
0x4000
0x12
0x4001
0x34
32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址
存放内容
0x4000
0x78
0x4001
0x56
0x4002
0x34
0x4003
0x12
而在Big-endian模式CPU内存中的存放方式则为:
内存地址
存放内容
0x4000
0x12
0x4001
0x34
0x4002
0x56
0x4003
0x78
联合体union的存放顺序是所有成员都从低地址开始存放,面试者的解答利用该特性,轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写。
如果谁能当场给出这个解答,那简直就是一个天才的程序员。
试题2:
写一个函数返回1+2+3+…+n的值(假定结果不会超过长整型变量的范围)
解答:
intSum(intn)
{
return((long)1+n)*n/2; //或return(1l+n)*n/2;
}
剖析:
对于这个题,只能说,也许最简单的答案就是最好的答案。
下面的解答,或者基于下面的解答思路去优化,不管怎么“折腾”,其效率也不可能与直接return(1l+n)*n/2相比!
intSum(intn)
{
longsum=0;
for(inti=1;i<=n;i++)
{
sum+=i;
}
returnsum;
}
所以程序员们需要敏感地将数学等知识用在程序设计中。
17):
什么是引用,引用与指针有什么区别?
1)引用必须被初始化,指针不必。
2)引用初始化以后不能被改变,指针可以改变所指的对象。
3)不存在指向空值的引用,但是存在指向空值的指针。
18):
A.c和B.c两个c文件中使用了两个相同名字的static变量,编译的时候会不会有问题?
这两个static变量会保存到哪里(栈还是堆或者其他的)?
static的全局变量,表明这个变量仅在本模块中有意义,不会影响其他模块。
他们都放在数据区,但是编译器对他们的命名是不同的。
如果要使变量在其他模块也有意义的话,需要使用extern关键字。
19):
什么是二叉树,平衡二叉树?
左右子树都是平衡二叉树且左右子树的深度差值的绝对值不大于1
20):
对于一个频繁使用的短小的函数,C/C++分别用什么实现。
C用宏定义实现,C++用INLINE实现。
21):
确定软件的功能和软件的接口是在那个环节实现的。
概要设计阶段。
22):
Heap和stack有什么区别?
Heap是堆,stack是栈。
栈的空间是由操作系统分配和释放的,而堆是由用户手动分配释放的。
Mallocnew分配的内存空间就是在堆上。
而程序在编译期对变量和函数分配内存就是在栈上,且程序运行时函数调用传递的参数也是在栈上进行的。
23).给两个变量,如何找出一个带环单链表中是什么地方出现环的?
一个递增一,一个递增二,他们指向同一个接点时就是环出现的地方
1)。
.堆栈溢出一般是由什么原因导致的?
没有回收垃圾资源
3).不能做switch()的参数类型是:
switch的参数不能为实型。
4)、队列和栈有什么区别?
队列先进先出,栈后进先出
七:
位域:
有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。
例如在存放一个开关量时,只有0和1两种状态,用一位二进位即可。
为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。
所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。
每个域有一个域名,允许在程序中按域名进行操作。
这样就可以把几个不同的对象用一个字节的二进制位域来表示。
一、位域的定义和位域变量的说明。
位域定义与结构定义相仿,其形式为:
struct位域结构名
{位域列表};
其中位域列表的形式为:
类型说明符位域名:
位域长度
例如:
structbs
{
inta:
8;
intb:
2;
intc:
6;
};
位域变量的说明与结构变量说明的方式相同。
可采用先定义后说明,同时定义说明或者直接说明这三种方式。
例如:
structbs
{
inta:
8;
intb:
2;
intc:
6;
}data;
说明data为bs变量,共占两个字节。
其中位域a占8位,位域b占2位,位域c占6位。
对于位域的定义尚有以下几点说明:
1.一个位域必须存储在同一个字节中,不能跨两个字节。
如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。
也可以有意使某位域从下一单元开始。
例如:
structbs
{
unsigneda:
4
unsigned:
0/*空域*/
unsignedb:
4/*从下一单元开始存放*/
unsignedc:
4
}
在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。
2.由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。
3.位域可以无位域名,这时它只用来作填充或调整位置。
无名的位域是不能使用的。
例如:
structk
{
inta:
1
int:
2/*该2位不能使用*/
intb:
3
intc:
2
};
从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的。
23)关于floatx与零值比较的if语句。
X!
=0;
If(x>0.000001||x<-0.000001)
X==0
If(x<0.000001&&x>-0.000001)
24)internet采用哪种网络协议,该协议的主要层次是什么?
Tcp/IP主要层次有应用层传输层网络层数据链路层物理层
ISO的七层模型是什么?
应用层表示层会话层传输层网络层物理链路层物理层
Tcpudp属于运输层
Tcp服务提供了数据流传输,可靠性,有效流控制,全双工操作和多路复用技术
Udp不提供可靠性,流控制以及错误恢复功能udp头包含少,负载消耗小。
优缺点:
Tcp提供可靠的传输服务,有流量控制。
缺点是包头大,冗余性不好。
Udp不提供稳定的服务但包头小开销小。
Internet物理地址和ip地址转换采用什么协议?
ARP(地址解析协议)
IP地址的编码分为那两部分?
网络号和主机号不过要与子网