嵌入式软件工程师笔试题Word文档下载推荐.docx
《嵌入式软件工程师笔试题Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《嵌入式软件工程师笔试题Word文档下载推荐.docx(13页珍藏版)》请在冰豆网上搜索。
![嵌入式软件工程师笔试题Word文档下载推荐.docx](https://file1.bdocx.com/fileroot1/2023-1/21/93ce7667-ed1f-4ccd-8bdb-0692710808e6/93ce7667-ed1f-4ccd-8bdb-0692710808e61.gif)
/*Filename:
*Cheaderfile
*/#ifndefHELLO_H#defineHELLO_H#ifdef__cplusplusextern"
C"
{#endif
voidprint_hello();
#ifdef__cplusplus}#endif#endif
*Csourcefile.
*/#include"
"
#include<
voidprint_hello(){
puts("
Hello,world!
);
}
*C++sourcefile.
intmain(){
print_hello();
return0;
建立一个新的目录,然后把这三个文件拷贝到目录中,也把Makefile文件拷贝到目录中。
之后,对Makefile的相关项目进行如下设置:
PROGRAM
:
=hello
#设置运行程序名SRCDIRS
=.
#源程序位于当前目录下SRCEXTS
=.c.cxx
#源程序文件有.c和.cxx两种类型CFLAGS
:
=-g
#为C目标程序包含GDB可用的调试信息CXXFLAGS
#为C++目标程序包含GDB可用的调试信息
由于这个简单的程序只使用了C标准库的函数(puts),所以对于CFLAGS和CXXFLAGS没有过多的要求,LDFLAGS和CPPFLAGS选项也无需设置。
经过上面的设置之后,执行make命令就可以编译程序了。
如果没有错误出现的话,./hello就可以运行程序了。
如果修改了源程序的话,可以看到只有和修改有关的源文件被编译。
也可以再为程序添加新的源文件,只要它们的扩展名是已经在Makefile中设置过的,那么就没有必要修改 Makefile。
1.引言 本文的写作目的并不在于提供C/C++程序员求职面试指导,而旨在从技术上分析面试题的内涵。
文中的大多数面试题来自各大论坛,部分试题解答也参考了网友的意见。
许多面试题看似简单,却需要深厚的基本功才能给出完美的解答。
企业要求面试者写一个最简单的strcpy函数都可看出面试者在技术上究竟达到了怎样的程度,我们能真正写好一个strcpy函数吗?
我们都觉得自己能,可是我们写出的strcpy很可能只能拿到10分中的2分。
读者可从本文看到strcpy函数从2分到10分解答的例子,看看自己属于什么样的层次。
此外,还有一些面试题考查面试者敏捷的思维能力。
分析这些面试题,本身包含很强的趣味性;
而作为一名研发人员,通过对这些面试题的深入剖析则可进一步增强自身的内功。
2.找错题 试题1:
voidtest1(){ charstring[10];
char*str1="
09"
;
strcpy(string,str1);
}
试题2:
voidtest2(){ charstring[10],str1[10];
inti;
for(i=0;
i<
10;
i++) { str1[i]='
a'
} strcpy(string,str1);
试题3:
voidtest3(char*str1){ charstring[10];
if(strlen(str1)<
=10) { strcpy(string,str1);
}}
解答:
试题1字符串str1需要11个字节才能存放下(包括末尾的’\0’),而string只有10个字节的空间,strcpy会导致数组越界;
对试题2,如果面试者指出字符数组str1不能在数组内结束可以给3分;
如果面试者指出strcpy(string,str1)调用使得从str1内存起复制到string内存起所复制的字节数具有不确定性可以给7分,在此基础上指出库函数strcpy工作方式的给10分;
对试题3,if(strlen(str1)<
=10)应改为if(strlen(str1)<
10),因为strlen的结果未统计’\0’所占用的1个字节。
剖析:
考查对基本功的掌握:
(1)字符串以’\0’结尾;
(2)对数组越界把握的敏感度;
(3)库函数strcpy的工作方式,如果编写一个标准strcpy函数的总分值为10,下面给出几个不同得分的答案:
2分
voidstrcpy(char*strDest,char*strSrc){ while((*strDest++=*strSrc++)!
=‘\0’);
4分
voidstrcpy(char*strDest,constchar*strSrc)0’
0’
.
功题 试题1:
分别给出BOOL,int,float,指针变量与“零值”比较的if语句(假设变量名为var) 解答:
BOOL型变量:
if(!
var) int型变量:
if(var==0) float型变量:
constfloatEPSINON=;
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分。
以下为WindowsNT下的32位C++程序,请计算sizeof的值
voidFunc(charstr[100]){ sizeof(str)=?
}void*p=malloc(100);
sizeof(p)=?
sizeof(str)=4sizeof(p)=4
Func(charstr[100])函数中数组名作为函数形参时,在函数体内,数组名失去了本身的内涵,仅仅只是一个指针;
在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。
数组名的本质如下:
(1)数组名指代一种数据结构,这种数据结构就是数组;
例如:
charstr[10];
cout<
<
sizeof(str)<
endl;
输出结果为10,str指代数据结构char[10]。
(2)数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改;
str++;
.*/#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"
来解决名字匹配问题,函数声明前加上extern"
后,则编译器就会按照C语言的方式将该函数编译为_foo,这样C语言中就可以调用C++的函数了。
试题5:
编写一个函数,作用是把一个char组成的字符串循环右移n个。
比如原来是“abcdefghi”如果n=2,移位后应该是“hiabcdefgh” 函数头是这样的:
.}
正确解答1:
voidLoopMove(char*pStr,intsteps){ intn=strlen(pStr)-steps;
chartmp[MAX_LEN];
巧题 试题1:
请写一个C函数,若处理器是Big_endian的,则返回0;
若是Little_endian的,则返回 解答:
intcheckCPU(){ { unionw { inta;
charb;
}c;
=1;
0X00000001四个字节,为一个字节 return==1);
大端的为00,小端的为01?
?
}
1l
1l
void的出现只是为了一种抽象的需要,如果你正确地理解了面向对象中“抽象基类”的概念,也很容易理解void数据类型。
正如不能给抽象基类定义一个实例,我们也不能定义一个void(让我们类比的称void为“抽象数据类型”)变量。
关于CONST的用法
const在C语言中算是一个比较新的描述符,我们称之为常量修饰符,意即其所修饰的对象为常量(immutable)。
我们来分情况看语法上它该如何被使用。
1、函数体内修饰局部变量。
例:
voidfunc(){constinta=0;
}首先,我们先把const这个单词忽略不看,那么a是一个int类型的局部自动变量,我们给它赋予初始值0。
然后再看const.const作为一个类型限定词,和int有相同的地位。
constinta;
intconsta;
是等价的。
于是此处我们一定要清晰的明白,const修饰的对象是谁,是a,和int没有关系。
const要求他所修饰的对象为常量,不可被改变,不可被赋值,不可作为左值(l-value)。
这样的写法也是错误的。
a=0;
这是一个很常见的使用方式:
constdoublepi=;
在程序的后面如果企图对pi再次赋值或者修改就会出错。
然后看一个稍微复杂的例子。
constint*p;
还是先去掉const修饰符号。
注意,下面两个是等价的。
int*p;
int*p;
其实我们想要说的是,*p是int类型。
那么显然,p就是指向int的指针。
同理constint*p;
其实等价于constint(*p);
intconst(*p);
即,*p是常量。
也就是说,p指向的数据是常量。
于是p+=8;
代表什么。
如果去掉const,我们可以看出char*argv[];
argv是一个数组,它的每个元素都是char*类型的指针。
如果加上const.那么const修饰的是谁呢?
他修饰的是一个数组,argv[],意思就是说这个数组的元素是只读的。
那么数组的元素的是什么类型呢?
是char*类型的指针.也就是说指针是常量,而它指向的数据不是。
于是argv[1]=NULL;
用extern例如/**/externconstdoublepi;
/**/constdoublepi=;
然后其他需要使用pi这个变量的,包含#include"
或者,自己把那句声明复制一遍就好。
这样做的结果是,整个程序链接完后,所有需要使用pi这个变量的共享一个存储区域。
2.使用static,静态外部存储类/**/staticconstpi=;
需要使用这个变量的*.c文件中,必须包含这个头文件。
前面的static一定不能少。
否则链接的时候会报告说该变量被多次定义。
这样做的结果是,每个包含了的*.c文件,都有一份该变量自己的copy,该变量实际上还是被定义了多次,占用了多个存储空间,不过在加了static关键字后,解决了文件间重定义的冲突。
坏处是浪费了存储空间,导致链接完后的可执行文件变大。
但是通常,这个,小小几字节的变化,不是问题。
好处是,你不用关心这个变量是在哪个文件中被初始化的。
最后,说说const的作用。
const的好处,是引入了常量的概念,让我们不要去修改不该修改的内存。
直接的作用就是让更多的逻辑错误在编译期被发现。
所以我们要尽可能的多使用const。
但是很多人并不习惯使用它,更有甚者,是在整个程序编写/调试完后才补const。
如果是给函数的声明补const,尚好。
如果是给全局/局部变量补const,那么……那么,为时已晚,无非是让代码看起来更漂亮了。
c语言中的结构(struct)和联合(union)简介
联
合(union)
1.联合说明和联合变量定义
联合也是一种新的数据类型,它是一种特殊形式的变量。
联合说明和联合变量定义与结构十分相似。
其形式为:
union联合名{
数据类型成员名;
...
}联合变量名;
联合表示几个变量公用一个内存位置,在不同的时间保存不同的数据类型和不同长度的变量。
下例表示说明一个联合a_bc:
uniona_bc{
inti;
charmm;
};
再用已说明的联合可定义联合变量。
例如用上面说明的联合定义一个名为lgc的联合变量,可写成:
uniona_bclgc;
在联合变量lgc中,整型量i和字符mm公用同一内存位置。
当一个联合被说明时,编译程序自动地产生一个变量,其长度为联合中最大的变量长度。
联合访问其成员的方法与结构相同。
同样联合变量也可以定义成数组或指针,但定义为指针时,也要用"
->
符号,此时联合访问成员可表示成:
联合名->
成员名
另外,联合既可以出现在结构内,它的成员也可以是结构。
例如:
struct{
intage;
char*addr;
union{
char*ch;
}x;
}y[10];
若要访问结构变量y[1]中联合x的成员i,可以写成:
y[1].;
若要访问结构变量y[2]中联合x的字符串指针ch的第一个字符可写成:
*y[2].;
若写成"
y[2].x.*ch;
是错误的。
2.结构和联合的区别
结构和联合有下列区别:
1.结构和联合都是由多个不同的数据类型成员组成,但在任何同一时刻,联合转只存放了一个被选中的成员,而结构的所有成员都存在。
2.对于联合的不同成员赋值,将会对其它成员重写,原来成员的值就不存在了,而对于结构的不同成员赋值是互不影响的。
下面举一个例了来加对深联合的理解。
例4:
main(){
union{
/*定义一个联合*/
struct{
/*在联合中定义一个结构*/
charfirst;
charsecond;
}half;
}number;
=0x4241;
/*联合成员赋值*/
printf("
%c%c\n"
'
/*联合中结构成员赋值*/
b'
%x\n"
;
getch();
}
输出结果为:
AB
6261
从上例结果可以看出:
当给i赋值后,其低八位也就是first和second的值;
当给first和second赋字符后,这两个字符的ASCII码也将作为i的低八位和高八位。
Volatile关键字告诉编译器不要持有变量的临时性拷贝。
一般用在多线程程序中,以避免在其中一个线程操作该变量时,将其拷贝入寄存器。
请看以下情形:
A线程将变量复制入寄存器,然后进入循环,反复检测寄存器的值是否满足一定条件(它期待B线程改变变量的值。
在此种情况下,当B线程改变了变量的值时,已改变的值对其在寄存器的值没有影响。
所以A线程进入死循环。
volatile就是在此种情况下使用。
一、预备知识—程序的内存分配
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。
其操作方式类似于数据结构中的栈。
2、堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。
注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
-程序结束后有系统释放
4、文字常量区—常量字符串就是放在这里的。
程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。
例子程序这是一个前辈写的,非常详细另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。
但是速度快,也最灵活。
堆和栈中的存储内容栈:
在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。
注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:
一般是在堆的头部用一个字节存放堆的大小。
堆中的具体内容有程序员安排。
存取效率的比较chars1[]="
aaaaaaaaaaaaaaa"
char*s2="
bbbbbbbbbbbbbbbbb"
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:
#includevoidmain(){chara=1;
charc[]="
90"
char*p="
a=c[1];
a=p[1];
return;
}对应的汇编代码10:
a=c[1];
004010678A4DF1movcl,byteptr[ebp-0Fh]0040106A884DFCmovbyteptr[ebp-4],cl11:
a=p[1];
0040106D8B55ECmovedx,dwordptr[ebp-14h]004010708A4201moval,byteptr[edx+1]004010738845FCmovbyteptr[ebp-4],al第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。
小结堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。
windows进程中的内存结构在阅读本文之前,如果你连是什么多不知道的话,请先阅读文章后面的。
接触过的人都知道,高级语言都能通过变量名来访问内存中的数据。
那么这些变量在内存中是如何存放的呢?
程序又是如何使用这些变量的呢?
下面就会对此进行深入的讨论。
下文中的C语言代码如没有特别声明,默认都使用VC编译的release版。
首先,来了解一下C语言的变量是如何在内存分部的。
C语言有全局变量(Global)、本地变量(Local),静态变量(Static)、寄存器变量(Regeister)。
每种变量都有不同的分配方式。
先来看下面这段代码:
intg1=0,g2=0,g3=0;
intmain(){staticints1=0,s2=0,s3=0;
intv1=0,v2=0,v3=0;
7c7c6c3”2”1”0C0000000C4C0040100F894C83C43C0000003C00401078C000C6A6A6A0000000C004068c