嵌入式笔试题.docx
《嵌入式笔试题.docx》由会员分享,可在线阅读,更多相关《嵌入式笔试题.docx(13页珍藏版)》请在冰豆网上搜索。
嵌入式笔试题
第一章C语言常见问题
1.1关作键字static的用
这个简单的问题很少有人能回答完全。
在C语言中,关键字static有三个明显的作用:
1)在函数体内,一个被声明为静态的变量在这一函数被调用过程中维持其值不变(该变量存放在静态变量区)。
2)在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。
它是一个本地的全局变量。
3)在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。
那就是,这个函数被限制在声明它的模块的本地范围内使用。
大多数应试者能正确回答第一部分,一部分能正确回答第二部分,但是很少的人能懂得第三部分。
这是一个应试者的严重的缺点,
因为他显然不懂得本地化数据和代码范围的好处和重要性。
考点:
在嵌入式系统中,要时刻懂得移植的重要性,程序可能是很多程序员共同协作同时完成,在定义变量及函数的过程,可能会重名,这给系统的集成带来麻烦,因此保证不冲突的办法是显示的表示此变量或者函数是本地的,static即可。
在Linux的模块编程中,这一条很明显,所有的函数和全局变量都要用static关键字声明,将其作用域限制在本模块内部,与其他模块共享的函数或者变量要EXPORT到内核中。
1)设置变量的存储域,函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
(2)限制变量的作用域,在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
(3)限制函数的作用域,在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
(4)在类中的static成员变量意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见;
(5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
1.2程序运行时的内存分配
一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。
其操作方式类似于数据结构中的栈。
2、堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。
注意它与数据结构中的堆是两回事,分配方式类似于链表。
3、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
- 程序结束后由系统释放。
4、文字常量区—常量字符串就是放在这里的。
程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。
二、例子程序
这是一个前辈写的,非常详细
//main.cpp
inta=0;全局初始化区
char*p1;全局未初始化区
main()
{
intb;栈
chars[]="abc";栈
char*p2;栈
char*p3="123456";123456\0在常量区,p3在栈上。
staticintc=0;全局(静态)初始化区
p1=(char*)malloc(10);
p2=(char*)malloc(20);分配得来得10和20字节的区域就在堆区。
strcpy(p1,"123456");123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}
1.3堆和栈的区别
1)申请方式
stack:
由系统自动分配。
例如,声明在函数中一个局部变量intb;系统自动在栈中为b开辟空间
heap:
需要程序员自己申请,并指明大小,在c中malloc函数
如p1=(char*)malloc(10);
在C++中用new运算符
如p2=(char*)malloc(10);
但是注意p1、p2本身是在栈中的。
堆是向上增长而栈是向下增长
2)堆栈的进一步讨论
在学习《深入理解计算机系统》中链接这一章中,数据讲一个可执行文件包含多个段。
在Linux系统中代码段总是从0x08048000处开始,数据段在接下来的4KB对齐的地址处,运行时堆在接下来的读写段之后的第一个4KB对齐的地址处,
并通过调用malloc库网上增长,开始于地址0x40000000处的段是为共享库保留的,用户栈总是从地址0xbfffffff处开始,并向下增长,从栈的上部开始于地址0xc0000000处的段是为操作系统驻留存储器部分的代码和数据保留的。
如下图:
下面通过代码来测试:
#include
inta1=0;
staticintsa=0;
inta2=0;
intb;
intmain()
{
intc1=1;
intc2;
int*d=(int*)malloc(sizeof(int)*10);
int*e=(int*)malloc(sizeof(int)*10);
staticintf=0;
if(d==NULL)
{
printf("mallocerror");
return-1;
}
printf("a1:
%p\n",&a1);
printf("sa:
%p\n",&sa);
printf("a2:
%p\n",&a2);
printf("b:
%p\n",&b);
printf("c1:
%p\n",&c1);
printf("c2:
%p\n",&c2);
printf("d:
%p\n",&d);
printf("*d:
%p\n",d);
printf("e:
%p\n",&e);
printf("*e:
%p\n",e);
printf("f:
%p\n",&f);
printf("%p\n",&"123");
free(d);
return0;
}
分析如下:
a1,a2,sa,f是按序存放,且全局共有非静态变量和静态变量是分开存放的,紧接着存放的是全局非初始化变量b,由于是int型,故地址相差4.
c1,c2,d,e这几个变量存储在用户栈空间。
主要区分d、e和d、e申请内存之间的区别。
*d,*e代表申请的内存,地址是增大方向,即地址是向上增长的,为堆中申请的数据。
“123”为常量,地址0x80486da应该在只读段的范围内,属于rodate数据。
故可知:
C程序中,全局变量和静态变量存储在读写段,常量存储在制度段,动态申请的内容是在堆上,局部变量在运行时栈中。
1.4关键字const的含义
只要能说出const意味着"只读"就可以了。
尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。
(如果你想知道更详细的答案,仔细读一下Saks的文章吧。
)
如果应试者能正确回答这个问题,我将问他一个附加的问题:
下面的声明都是什么意思?
Const只是一个修饰符,不管怎么样a仍然是一个int型的变量
constinta;
intconsta;
constint*a;
int*consta;
intconst*aconst;
本质:
const在谁后面谁就不可修改,const在最前面则将其后移一位即可,二者等效
前两个的作用是一样,a是一个常整型数。
第三个意味着a是一个指向常整型数的指针(也就是,指向的整型数是不可修改的,但指针可以,此最常见于函数的参数,当你只引用传进来指针所指向的值时应该加上const修饰符,程序中修改编译就不通过,可以减少程序的bug)。
第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。
const关键字至少有下列n个作用:
(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显然不符合编程者的初衷,也没有任何意义。
1.5关键字volatile含义
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
下面是volatile变量的几个例子:
1.6什么是野指针?
产生的原因?
“野指针”不是NULL指针,是指向“垃圾”内存(不可用内存)的指针。
“野指针”是很危险的,if无法判断一个指针是正常指针还是“野指针”。
有个良好的编程习惯是避免“野指针”的唯一方法。
野指针的成因主要有三种:
一、指针变量没有被初始化。
指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
二、指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。
三、指针操作超越了变量的作用范围。
比如不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。
比如说某个地址的生命期,使用一个没有生命期的指针是非常危险的。
1.7sizeof和strlen区别
一、sizeof
sizeof(...)是运算符,在头文件中typedef为unsignedint,其值在编译时即计算好了,参数可以是数组、指针、类型、对象、函数等。
它的功能是:
获得保证能容纳实现所建立的最大对象的字节大小。
二、strlen
strlen(...)是函数,要在运行时才能计算。
参数必须是字符型指针(char*)。
当数组名作为参数传入时,实际上数组就退化成指针了。
它的功能是:
返回字符串的长度。
该字符串可能是自己定义的,也可能是内存中随机的,该函数实际完成的功能是从代表该字符串的第一个地址开始遍历,直到遇到结束符NULL。
返回的长度大小不包括NULL。
三、举例:
eg1、chararr[10]="What?
";
intlen_one=strlen(arr);
intlen_two=sizeof(arr);
cout<输出结果为:
5and10
点评:
sizeof返回定义arr数组时,编译器为其分配的数组空间大小,不关心里面存了多少数据。
strlen只关心存储的数据内容,不关心空间的大小和类型。
其中\0不计入strlen计数中去。
第二章 C语言的常见笔试题
2.1 在某工程中,要求设置一绝对地址为
嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。
在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。
编译器是一个纯粹的ANSI编译器。
写代码去完成这一任务。
int*ptr;
ptr=(int*)0x67a9;
*ptr=0xaa55;
2.2 写出strcpy,strcmp,strlen,memcpy函数源码的实现。
char*strcpy(char*strDest,constchar*strSrc)
{
assert((strDest!
=NULL)&&(strSrc!
=NULL));
char*address=strDest;
while((*strDest++=*strSrc++)!
=‘\0’);
returnaddress;
}
intstrlen(constchar*str)//输入参数const
{
assert(strt!
=NULL);//断言字符串地址非0
intlen;
while((*str++)!
='\0')
{
len++;
}
returnlen;
}
2.3 请写一个C函数,判断大小端。
所谓的大端模式,是指数据的高位,保存在内存的低地址中,而数据的低位,保存在内存的高地址中
所谓的小端模式,是指数据的高位保存在内存的高地址中,而数据的低位保存在内存的低地址中
intcheckCPU()
{
{
unionw
{
inta;
charb;
}c;
c.a=1;
return(c.b==1);
}
}
2.4用变量a给出下面的定义
a)一个整型数(Aninteger)
b)一个指向整型数的指针(Apointertoaninteger)
c)一个指向指针的的指针,它指向的指针是指向一个整型数(Apointertoapointertoanintege)r
d)一个有10个整型数的数组(Anarrayof10integers)
e)一个有10个指针的数组,该指针是指向一个整型数的。
(Anarrayof10pointerstointegers)
f)一个指向有10个整型数数组的指针(Apointertoanarrayof10integers)
g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数(Apointertoafunctionthattakesanintegerasanargument
andreturnsaninteger)
h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数(Anarrayoftenpointerstofunctionst
hattakeanintegerargumentandreturnaninteger)
答案是:
a)inta;//Aninteger
b)int*a;//Apointertoaninteger
c)int**a;//Apointertoapointertoaninteger
d)inta[10];//Anarrayof10integers
e)int*a[10];//Anarrayof10pointerstointegers
f)int(*a)[10];//Apointertoanarrayof10integers
g)int(*a)(int);//Apointertoafunctionathattakesanintegerargumentandreturnsaninteger
h)int(*a[10])(int);//Anarrayof10pointerstofunctionsthattakeanintegerargumentandreturnaninteger
2.5 位操作
嵌入式系统总是要用户对变量或寄存器进行位操作。
给定一个整型变量a,写两段代码,第一个设置a的bit3,第二个清除a的bit3。
在以上两个操作中,要保持其它位不变。
#defineBIT3(0x1<<3)
staticinta;
voidset_bit3(void){
a|=BIT3;
}
voidclear_bit3(void){
a&=~BIT3;
}#defineBIT3(0x1<<3)
staticinta;
voidset_bit3(void){
a|=BIT3;
}
voidclear_bit3(void){
a&=~BIT3;
}
2.6 与“零值”比较
分别给出BOOL,int,float,指针变量与“零值”比较的 if语句(假设变量名为var)
解答:
BOOL 型变量:
if(!
var)
int型变量:
if(var==0)
float 型变量:
constfloatEPSINON=0.00001;
if((x>=-EPSINON)&&(x<=EPSINON)
指针变量:
if(var==NULL)
2.7 对齐方式
·使用伪指令#pragmapack(n),编译器将按照n个字节对齐;
·使用伪指令#pragmapack(),取消自定义字节对齐方式。
注意:
如果#pragmapack(n)中指定的n大于结构体中最大成员的size,则其不起作用,结构体仍然按照size最大的成员进行对界。
另外,通过__attribute((aligned(n)))也可以让所作用的结构体成员对齐在n字节边界上。
2.8malloc分配的是物理地址还是虚拟地址
物理地址
2.9 不是使用变量,调换两个变量的值
inta=10,b=20;a=a+b;b=a-b;a=a-b;结果:
a=20;b=10;