嵌入式软件工程师笔试题.docx
- 文档编号:12115180
- 上传时间:2023-04-17
- 格式:DOCX
- 页数:27
- 大小:64.01KB
嵌入式软件工程师笔试题.docx
《嵌入式软件工程师笔试题.docx》由会员分享,可在线阅读,更多相关《嵌入式软件工程师笔试题.docx(27页珍藏版)》请在冰豆网上搜索。
嵌入式软件工程师笔试题
维数组空间的动态申请
a.简单的,已经有一维,如
char(*c)[5];
c=newchar[n][5];维的
int**p;
p=newint*[m_row];
题二:
一
读程序,写出运行结果
#inelude<>
#include<>
#include<>
#include<>
typedefstruct
{
intvalue;
/chartype;
}head_t;这是什么东西啊?
typedefstruct
{
head_thead;
intpara;
}message_t;
voidmain(void)
{
message_t*message二NULL;
\head_t*head=NULL;
message=(message_t*)malloc(sizeof(message_t));
oassert(message);目标文件,不进行连接(一般很少单独使用)。
omakeclean
删除编译产生的目标文件和依赖文件。
omakecleanail
删除目标文件、依赖文件以及可执行文件。
omakerebuild
重新编译和连接程序。
相当于makeclean&&makeall。
下面提供两个例子来具体说明上面Makefile的用法。
例一HelloWorld程序
这个程序的功能是输出Hello,world!
这样一行文字。
由、、三个文件组成。
前两
个文件是C程序,后一个是C++程序,因此这是一个C和C++混编程序。
代码:
/*Filename:
*Cheaderfile
*/#ifndefHELLOH
#define
HELLO_H
#ifdef__cplusplus\
extern"C"{#endif
void
print_hello();
#ifdef__cplusplus
}#endif#endif
代码:
/*Filename:
Csourcefile.
*/#inelude""
voidprint_hello()
puts("Hello,world!
");
#include<>}
代码:
int
/*Filename:
*C++sourcefile.*/#include""
main(){print_hello();return0;}
建立一个新的目录,然后把这三个文件拷贝到目录中,也把Makefile文件拷贝到目
录中。
之后,对Makefile的相关项目进行如卞设置:
代码:
PROGRAM:
=hello
SRCDIRS:
=.
SRCEXTS:
=.c.cxx
CFLAGS:
=-g
CXXFLAGS:
=-g信息
#设置运行程序名
#源程序位于当前目录下
#源程序文件有.c和.cxx两种类型
#为C目标程序包含GDB可用的调试信息
#为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[1O];inti;for(i=0;
i<10;i++){str1[i]='a';}strcpy(string,
stri);}
试题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'
功题
试题1:
分别给出BOOL,int,float,指针变量与零值”比较的if语句(假设变量名为var)/\
解答:
BOOL型变量:
if(!
var)|_Z\
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分。
\
试题2:
以下为WindowsNT下的32位C++程序,请计算sizeof的值\
voidFunc(charstr[100]){
sizeof(str)=?
}
void*p=|
malloc(100);sizeof(p)=?
解答:
sizeof(str)=4sizeof(p)=4
咅U析:
\一_一_一
Func(charstr[100])函数中数组名作为函数形参时,在函数体内,数组名失
去了本身的内涵,仅仅只是一个指针;在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。
X
数组名的本质如下:
/
(1)数组名指代一种数据结构,这种数据结构就是数组;
例如:
charstr[10];cout< 输出结果为10,str指代数据结构char[10]。 (2) charstr[10]; sM++; 数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改; 头文件中的编译宏 的作用是防止被重复引用。 作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。 函数被C++编译后在symbol库中的名字与C语言的不同。 例如,假设某个函数的原型为: 该函数被C编译器编译后在symbol库中的名字为_foo,而C++编译器则会产生像_foo」nt_int之类的名字。 _foo」nt_int这样的名字包含了函数名和函数参数数 量及类型信息,C++就是考这种机制来实现函数重载的。 voidfoo(intx,inty); 为了实现C和C++的混合编程,C++提供了C连接交换指定符号extern"C"来解决名字匹配问题,函数声明前加上extern"C"后,则编译器就会按照C语言的方式将该函数编译为_foo,这样C语言中就可以调用C++的函数了试题5: 编写一个函数,作用是把一个char组成的字符串循环右移n个。 比如原来是 “abcdefghi如果n=2,移位后应该是“hiabcdefgh 函数头是这样的: 解答: 正确解答1: voidLoopMove(char*pStr,intsteps){intn二strlen(pStr) -steps;chartmp[MAXLEN]; 巧题 试题1: 请写一个C函数,若处理器是Big_endian的,则返回0;若是 Little_endian的,则返回/ 解答: \ intcheckCPU(){{unionw{inta; charb;}c;=1;0X00000001四个字节,为一个字节 return==1);大端的为00,小端的为01? ? ? ? } 类”的概念,也很容易理解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)。 这样的写法也是错误的。 constinta; 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[1]=NULL;用extern 例如 /**/ externconstdoublepi; /**/ constdoublepi=; 然后其他需要使用pi这个变量的,包含 #include"" 或者,自己把那句声明复制一遍就好。 这样做的结果是,整个程序链接完后,所有需要使用pi这个变量的共享一个存储区域。 2.使用static,静态外部存储类 /**/ staticconstpi=; 的指针。 argv[],意思就是 char*类型的指 需要使用这个变量的*.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{ inti; char*ch; }x; 理叫、「冃 若要访问结构变量y[1]中联合x的成员i,可以写成: y[i].; 若要访问结构变量y[2]中联合x的字符串指针ch的第一个字符可写成: *y[2].; 若写成"y[2].x.*ch;"是错误的。 2.结构和联合的区别 结构和联合有下列区别: 1.结构和联合都是由多个不同的数据类型成员组成,但在任何同一时刻,联合转只存放了一个被选中的成员,而结构的所有成员都存在。 2.对于联合的不同成员赋值,将会对其它成员重写,原来成员的值就不存在了,而对于结构的不同成员赋值是互不影响的。 下面举一个例了来加对深联合的理解。 例4: main() { union{/*定义一个联合*/ inti; struct{/*在联合中定义一个结构*/ charfirst; charsecond; }half; }number; =0x4241;/*联合成员赋值*/ printf("%c%c\n", 'a';/*联合中结构成员赋值*/ 'b'; printf("%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(){char a=1;charc[]="90";char*p="90";a=c[1];a= p[1];return;}对应的汇编代码10: a=c[1];004010678A4DF1 movcl,byteptr[ebp-0Fh]0040106A884DFCmovbyteptr [ebp-4],cl11: a=p[1];0040106D8B55ECmovedx,dwordptr [ebp-14h]004010708A4201moval,byteptr[edx+1]0040107388 45FCmovbyteptr[ebp-4],al第一种在读取时直接就把字符串中的元素读到 寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。 小结堆和栈的区别可以用如下的比喻来看出: 使用栈就象我们去饭 馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。 使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。 windows进程中的内存结构在阅读本文之前,如 果你连是什么多不知道的话,请先阅读文章后面的。 接触过的人都知道,高级 语言都能通过变量名来访问内存中的数据。 那么这些变量在内存中是如何存放的呢? 程序又是如何使用这些变量的呢? 下面就会对此进行深入的讨论。 下文中的C 语言代码如没有特别声明,默认都使用VC编译的release版。 首先,来了解 一下C语言的变量是如何在内存分部的。 C语言有全局变量(Global)、本地变量 (Local),静态变量(Static)、寄存器变量(Regeister)。 每种变量都有不同的分配方式。 先来看下面这段代码: #inelude<>intg仁0,g2=0, g3=0;intmain(){staticints1=0,s2=0,s3=0;intv1=0,v2=0, v3=0;7c7c6c3”2”1”0C0000000C4C0040100F894C83C43C0000003 C00401078C000C6A6A6A0000000C004068c1M.);7c7c6c #include #includeviomanip> #include usingnamespacestd; intmain() { inta[2]={-1,1}; stringstr="*******************"・ inti=0,j=9,t=1; for(i=0;i<19;++i) { j+=a[i<10]; if(i! =0)t=t+a[i<10]*2; cout<
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 嵌入式 软件工程师 笔试