嵌入式软件工程师笔试题.docx
- 文档编号:7239338
- 上传时间:2023-01-22
- 格式:DOCX
- 页数:13
- 大小:28.31KB
嵌入式软件工程师笔试题.docx
《嵌入式软件工程师笔试题.docx》由会员分享,可在线阅读,更多相关《嵌入式软件工程师笔试题.docx(13页珍藏版)》请在冰豆网上搜索。
嵌入式软件工程师笔试题
维数组空间的动态申请 a.简单的,已经有一维,如char(*c)[5];c=newchar[n][5];维的int**p;p=newint*[m_row];.
题二:
读程序,写出运行结果
#include<>
#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删除编译产生的目标文件和依赖文件。
omakecleanall删除目标文件、依赖文件以及可执行文件。
omakerebuild重新编译和连接程序。
相当于makeclean&&makeall。
下面提供两个例子来具体说明上面Makefile的用法。
例一 HelloWorld程序这个程序的功能是输出Hello,world!
这样一行文字。
由、、三个文件组成。
前两个文件是C程序,后一个是C++程序,因此这是一个C和C++混编程序。
代码:
/*Filename:
*Cheaderfile */#ifndefHELLO_H#defineHELLO_H#ifdef__cplusplusextern"C"{#endif voidprint_hello();#ifdef__cplusplus}#endif#endif
代码:
/*Filename:
*Csourcefile. */#include""#include<>voidprint_hello(){ puts("Hello,world!
");}
代码:
/*Filename:
*C++sourcefile. */#include""intmain(){ print_hello(); return0;}
建立一个新的目录,然后把这三个文件拷贝到目录中,也把Makefile文件拷贝到目录中。
之后,对Makefile的相关项目进行如下设置:
代码:
PROGRAM :
=hello #设置运行程序名SRCDIRS :
=. #源程序位于当前目录下SRCEXTS :
=.c.cxx #源程序文件有.c和.cxx两种类型CFLAGS :
=-g #为C目标程序包含GDB可用的调试信息CXXFLAGS :
=-g #为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’
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分。
试题2:
以下为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< 输出结果为10,str指代数据结构char[10]。 (2)数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改; charstr[10];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"C"来解决名字匹配问题,函数声明前加上extern"C"后,则编译器就会按照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)。 这样的写法也是错误的。 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[],意思就是说这个数组的元素是只读的。 那么数组的元素的是什么类型呢? 是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{ inti; 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{ /*定义一个联合*/ 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(){chara=1;charc[]="90";char*p="90";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)。 每种变量都有不同的分配方式。 先来看下面这段代码: #include<>intg1=0,g2=0,g3=0;intmain(){staticints1=0,s2=0,s3=0;intv1=0,v2=0,v3=0;7c7c6c3”2”1”0C0000000C4C0040100F894C83C43C0000003C00401078C000C6A6A6A0000000C004068c
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 嵌入式 软件工程师 笔试