怎么用c写嵌入式系统开发的死循环的

嵌入式C程序员应该知道的几个基本问题收藏_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
嵌入式C程序员应该知道的几个基本问题收藏
上传于|0|0|文档简介
&&嵌入式C程序员应该知道的几个基本问题收藏
你可能喜欢嵌入式程序员应知道的0x10个C语言Tips - 又一个暑假的博客 -
中国电子技术论坛 -
最好最受欢迎电子论坛!
嵌入式程序员应知道的0x10个C语言Tips
已有 230 次阅读 09:54
01/*002*******************************************************************************************************003**&&&&&&&&&&&&&&&&&&&&&&&& 嵌入式程序员应知道的0x10个C语言Tips004*******************************************************************************************************005&&&&&&&&006&&&&C语言是衡量嵌入式系统程序员必须而且有效的方法。这些年,我既参加也组织了许多测试,我意识到这些测试007能为interviewer(面试官)和interviewee(被面试者)提供许多有用信息。此外,撇开面试的压力不谈,这种测试也是008相当有趣的。009&&&&从interviewee的角度来讲,你能了解许多关于出题者或面试官的情况。比如,这些测试很可能只是interviewer010为了彰显其对ANSI标准细节的熟知而不是探究某些技术技巧设计出来的。再如,要你答出某个字符的ASCII值、考察011你关于系统调用和内存分配策略方面的能力……这标志着出题者很可能花了大量时间在微机上而不上在嵌入式系统上。012如果确实是这样的话,我得认真考虑我是否应该去做这份工作。013&&&&从interviewer的角度来讲,一个测试也许能从多方面揭示应试者的素质:最基本的,你能了解应试者C语言的水014平。不管怎么样,看一下这人如何回答他不会的问题也是满有趣的。应试者是以好的直觉做出明智的回答,还是只是015瞎蒙呢?当应试者在某个问题上卡住时是找借口呢,还是表现出对问题的真正的好奇心,把这看成学习的机会呢?我016发现这些信息与他们的测试成绩一样有用。017&&&&有了这些想法,我决定出一些真正针对嵌入式系统的考题,希望这些令人头痛的考题能给应试者一点帮住。这些018问题都是我这些年实际遇到的。其中有些问题很难,但它们应该都能给你一点启迪。019&&&&这些问题有一定的区分的度:大多数初级水平的应试者的成绩会很差,而经验丰富的程序员应该有很好的成绩。020&021&022预处理器(Preprocessor)023&0241. 请您用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年的情况)。025&026&&&&#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL027&028我在这想看到几件事情:029&&&&o; #define语法的基本知识(例如:不能以分号结束,括号的使用,等等)。030&&&&o; 知道预处理器会为你计算表达式的值的,因此,直接写出你是如何计算一年中有多少秒而不需要计算出实际031&&&&&&&的值,这样代码更清晰、更可读。032&&&&o; 知道这个表达式将使一个16位机的整型数溢出,因此要用到长整型符号L,告诉编译器这个常数是个长整型数。033&&&&o; 如果你的表达式中用到UL(表示无符号长整型),说明你有了一个好的起点。记住,第一印象很重要。034&0352. 写一个宏定义MIN,这个宏返回两个参数中较小(含相等的情况)的一个。036&037&&&&#define& MIN(A,B)&& ((A) &= (B) ? (A) : (B))038&039这个测试是为下面的目的而设的:040&&&&o; 标识#define在宏中应用的基本知识。这是很重要的,因为直到嵌入(inline)操作符变为标准C的一部分,宏041&&&&&&&是产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。042&&&&o; 三目运算符(?:)的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-else更优化的代码,了043&&&&&&&解这个用法是很重要的。044&&&&o; 懂得在宏定义中小心地把参数用括号括起来。045&&&&o; 我也会用这个问题来讨论宏的副作用,例如:当你写下面的代码时会出现什么问题?046&047&&&&&&&&least = MIN(*p++, b);048&0493. 预处理指令#error的作用是什么?050&051&&&&如果你不知道答案,请看参考文献1。这问题对区分一个正常的coder和一个书呆子是很有用的。只有书呆子才会052读C语言课本的附录去找出像这种问题的答案。(#error: 停止编译并显示错误信息)053&054死循环(Infinite loops)055&0564. 嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?057&058&&&&这个问题有几个解决方案。我的首选方案是:while(1) {……},然而一些程序员会喜欢这个方案:for(;;) {……}。059这种答案让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为060一个机会去探究他们这样做的基本原理,显然for(;;)比while(1)要执行更多的指令。如果他们的答案是:“我看别人061这样做的,从没有想到过为什么”,这会给我留下一个坏印象。还有一种方案是用goto语句:062&063&&&&Lable: ...064&&&&...065&&&&goto L066&067应试者如果给出这种方案,这说明他可能是一个汇编语言程序员(这也许是好事),或者他是一个想进入新领域的BASIC068或FORTRAN程序员。069&070数据声明(Data declarations)071&0725. 用变量a给出下面的定义:073&074&&&&a). 一个整型数075&&&&&&&&(An integer)076&&&&b). 一个指向整型数的指针077&&&&&&&&(A pointer to an integer)078&&&&c). 一个指向指针的指针,它指向的指针是指向一个整型数079&&&&&&&&(A pointer to a pointer to an integer)080&&&&d). 一个有10个整型数的数组081&&&&&&&&(An array of 10 integers)082&&&&e). 一个有10个指针的数组,指针均是指向整型数的083&&&&&&&&(An array of 10 pointers to integers)084&&&&f). 一个指向有10个整型数数组的指针085&&&&&&&&(A pointer to an array of 10 integers)086&&&&g). 一个指向函数的指针,该函数有一个整型参数并返回一个整型数087&&&&&&&&(A pointer to a function that takes an integer as an argument and returns an integer)088&&&&h). 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数089&&&&&&&&(An array of ten pointers to functions that take an integer argument and return an integer)090&091答案是:092&093&&&&a).094&&&&b). int *a;095&&&&c). int **a;096&&&&d). int a[10];097&&&&e). int *a[10];098&&&&f). int (*a)[10];099&&&&g). int (*a)(int);100&&&&h). int (*a[10])(int);101&102&&&&经常有人说这里有几个问题要翻一下书才能回答,我同意这种说法。我写这篇文章时,为了确定语法的正确性,103我的确也查了一下书。但是当我被面试的时候,我期望被问到这些问题(或者类似的问题)。因为在被面试的这段时104间里,我确定我知道这个问题的答案。应试者如果不知道所有的答案(或至少大部分答案),那么也就没有为这次面105试做准备,如果该面试者没有为这次面试做准备,那么他又能为什么出准备呢?106&107static108&1096. 关键字static的作用是什么?110&111&&&&这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:112&&&&o; static修饰局部变量时,表示希望这个局部变量的值在程序运行期间维持不变。113&&&&o; static修饰全局变量时,表示希望这个全局变量仅可以在本模块内被访问和引用,对其他模块不可见。114&&&&o; static修饰一个函数时,表示希望这个函数仅可以在本模块内被调用,对其他模块不可见。115&&&&大多数应试者能正确回答第一部分,一部分能正确回答第二部分,很少的人能懂得第三部分。这是一个应试者116的严重的缺陷,因为他显然不懂得本地化数据和代码的好处和重要性。117&118const119&1207.关键字const有什么含意?121&122&&&&我只要一听到interviewee说:“const修饰的变量意味着常量”,我就知道我正在和一个业余者打交道。去年123Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一124位读者应该非常熟悉const能做什么和不能做什么。如果你从没有读到那篇文章,只要能说出const意味着“只读”就125可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。如果你想知道更详细的答案,仔细读一126下Saks的文章吧。(译者:const修饰的是变量而不是常量,我们称其为“只读型变量”,其初始化之后只读不可写)。127&&&&如果应试者能正确回答这个问题,我将问他一个附加的问题:下面的声明都是什么意思?128&129&&&&130&&&&131&&&&const int *a;132&&&&int *133&&&&int const *134&135&&&&前两个的作用是一样,a是一个只读型整数。第三个意味着a是一个指向只读型整数的指针(也就是说,整数是不136可修改的,但指针可以)。第四个a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针137不可修改)。最后一个a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不138可修改的)。139&&&&如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,你可能会问,即使不用关键字140const,也可以写出功能正确的程序,那么我为什么还要如此重视关键字const呢?我有如下的几下理由:141&&&&o; 关键字const的作用是为了给你的代码的阅读者传达非常有用的信息。实际上,声明一个参数为常量是为了告142&&&&&&&诉了用户这个参数的应用目的。如果你曾花很多时间清理他人留下的垃圾,你就会很快学会感谢这点多余的143&&&&&&&信息。(当然,懂得用const的程序员很少会留下垃圾让别人来清理的。)144&&&&o; 给优化器一些附加信息,使用关键字const也许能产生更紧凑的代码。145&&&&o; 合理地使用关键字const可以使编译器很自然地保护那些不希望被修改(你很可能是无意地)的参数,防止其被146&&&&&&&修改。简而言之,这样可以减少bug的出现。147&148volatile149&1508. 关键字volatile有什么含意?并给出三个不同的例子。151&152&&&&一个定义为volatile的变量是说这变量可能会被意想不到的情况修改,告诫编译器不要去优化和这个变量有关153的代码。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取(有时也可能需要写入)这个变量的值,154而不是使用其保存在寄存器里的备份。下面是应用volatile变量的几个例子:155&&&&o; 硬件设备的某些寄存器(如:状态寄存器);156&&&&o; 某些ISR会访问到的非自动变量(Non-automatic variables);157&&&&o; 多线程应用中被几个任务共享的变量;158回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的159家伙们经常同硬件、中断、RTOS等等打交道,所有这些都会用到volatile变量。不懂volatile的用法将会带来灾难。160假设interviewee正确地回答了这个问题,我将稍加深究,看一下这家伙是不是直正理解了volatile的重要用法:161&&&&o; 一个参数既可以是const的也可以是volatile的吗?解释为什么。162&&&&o; 一个指针可以是volatile的吗?解释为什么。163&&&&o; 下面的函数有什么错误:164&165&&&&&&&&int square(volatile int *ptr)166&&&&&&&&{167&&&&&&&&&&&&return (*ptr) * (*ptr);168&&&&&&&&}169&170下面是答案:171&&&&o; 是的。一个例子是只读型寄存器。它是volatile的,因为要防止它可能被意想不到地修改。它是const的,因172&&&&&&&为程序不应该试图去修改它。173&&&&o; 是的。尽管这并不常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时,这个指正应该174&&&&&&&是volatile型的。175&&&&o; 这段代码有点变态。这段代码的目的是用来返指针ptr指向值的平方。但是,由于ptr指向一个volatile型参176&&&&&&&数,编译器将产生类似下面的代码(关键要注意到,编译器需要一次次认真地读取ptr所指向的值):177&&&&&&&&&178&&&&&&&&&&&&long square(volatile int *ptr)179&&&&&&&&&&&&{180&&&&&&&&&&&&&&&&int a,b;181&&&&&&&&&&&&&&&&a = *182&&&&&&&&&&&&&&&&b = *183&184&&&&&&&&&&&&&&&&return a *185&&&&&&&&&&&&}186&187&&&&&&&由于ptr指向的值可能会被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返回不是你所期望188&&&&&&&的平方值!正确的代码如下:189&190&&&&&&&&&&&&long square(volatile int *ptr)191&&&&&&&&&&&&{192&&&&&&&&&&&&&&&&193&&&&&&&&&&&&&&&&a = *194&195&&&&&&&&&&&&&&&&return a *196&&&&&&&&&&&&}197&198位操作(Bit manipulation)199&2009. 嵌入式系统总是要用户对变量或寄存器进行位操作。201&202&&&&给定一个整型变量a,写两段代码,第一个设置a的bit3,第二个清除a的bit3。在以上两个操作中,要保持其它位203不变。对这个问题有三种基本的反应:204&&&&o; 不知道如何下手。说明interviewee从没做过嵌入式工作。(一个嵌入式编程高手也必须得是一个位操作高手。)205&&&&o; 用bit fields。bit fields是被扔到C语言死角的东西,它保证你的代码在不同编译器之间是不可移植的,同时206&&&&&&&也保证了的你的代码是不可重用的。我最近不幸看到Infineon为其较复杂的通信芯片写的驱动程序,它用到了207&&&&&&&bit fields,因此完全对我无用,因为我的编译器用其它的方式来实现bit fields的。从道德上讲:永远不要让208&&&&&&&一个非嵌入式的家伙沾实际硬件的边。209&&&&o; 用#defines和bit masks(位掩码)操作。这是一个有极高可移植性的方法,值得推荐。最佳的解决方案如下:210&211&&&&&&&&#define BIT3 (0x1 && 3)212&213&&&&&&&&214&215&&&&&&&&void set_bit3(void)216&&&&&&&&{217&&&&&&&&&&&&a |= BIT3;218&&&&&&&&}219&220&&&&&&&&void clear_bit3(void)221&&&&&&&&{222&&&&&&&&&&&&a &= ~BIT3;223&&&&&&&&}224&225&&&&一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是完全可以接受的。总之我希望看到几个226要点:说明常数(如上述中的BIT3)、|= 和 &=~ 操作。227&22810. 嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。229&230&&&&在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa55。编译器是一个纯粹的ANSI编译器。写代码去231完成这一任务。232&&&&这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一个指针是合法的。这一问题的233实现方式随着个人风格不同而不同。典型的类似代码如下:234&235&&&&int *236&&&&ptr = (int *)0x67a9;237&&&&*ptr = 0xaa55;238&239另一个较晦涩的方法是:240&241&&&&*(int * const)(0x67a9) = 0xaa55;242&243即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。244&245中断(Interrupts)246&24711. 中断是嵌入式系统中重要的组成部分,这导致了很多编译器开发商提供一种扩展关键字,让标准C支持中断。248&249&&&&最可能的是,产生了一个新的关键字 __interrupt。(这里暂且以“__interrupt”为例子)。下面的代码就使用了250__interrupt关键字定义一个中断服务子程序(ISR),请评论一下这段代码的:251&252&&&&__interrupt double compute_area (double radius)253&&&&{254&&&&&&&&double area = PI * radius *255&&&&&&&&printf("/nArea = %f", area);256&&&&&&&&257&&&&}258&259这个函数有太多的错误了,让人不知从何说起了:260&&&&o; ISR不能返回一个值。如果你不懂这个,那么你不会被雇用的。261&&&&o; ISR不能含有形参。如果你没有看到这一点,你被雇用的机会等同第一项。262&&&&o; 在许多处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额外的寄存器入栈,有些处理器263&&&&&&&/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。264&&&&o; printf()经常存在重入性的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你连后两点也正265&&&&&&&确回答了,那么你被雇用前景越来越光明了。266&267代码例子(Code examples)268&26912. 下面的代码输出是什么,为什么?270&271&&&&void foo(void)272&&&&{273&&&&&&&&unsigned int a = 6;274&&&&&&&&int b = -20;275&&&&&&&&(a+b & 6) ? puts("& 6") : puts("&= 6");276&&&&}277&278答案是: ”&6”。279&&&&这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者关于这些东西懂得极少。问题的答案是输出280是 ”&6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非281常大的正整数(具体有多大,补码运算),所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌282入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。&&&283&28413. 评价下面的代码片断:285&286&&&&unsigned int zero = 0;287&&&&unsigned int compzero = 0xFFFF;288&289&&&&对于一个int型不是16位的处理器来说,上面的代码是不正确的。应该如下:290&291&&&&unsigned int compzero = ~0;& // 将0这个字(不管有多长)按位取反292&293&&&&这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里,好的嵌入式程序员能够非常准确地明白硬294件的细节和它的局限,然而PC机程序员往往把硬件作为一个无法避免的烦恼。295&296&&&&到了这里,应试者或者完全垂头丧气了或者信心满满志在必得。如果显然应试者不是很好,那么这个测试就在这里结束297了。但如果显然应试者做得不错,那么我就扔出下面的追加问题,这些问题是比较难的,我想仅仅只有非常优秀的应试者才298能做得不错。提出这些问题,我更多的是希望看到应试者应付问题的方法,而不是答案。不管如何,你就当是这个娱乐吧……299&300动态内存分配(Dynamic memory allocation)301&30214. 尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(heap)中动态分配内存的过程的。那么嵌入式系统中,303动态分配内存可能发生的问题是什么?这里,我期望应试者能提到内存碎片、碎片收集的问题、变量的持行时间等等。这个304主题已经在ESP杂志中被广泛地讨论过了(主要是 P.J. Plauger, 他的解释远远超过我这里能提到的任何解释),所以回过头305看一下这些杂志吧!306&&&&让应试者进入一种虚假的安全感觉后,我拿出这么一个小节目:下面的代码片段的输出是什么,为什么?307&&&&&&&&&308&&&&&&&&char *309&&&&&&&&if ((ptr = (char *)malloc(0)) == NULL) {310&&&&&&&&&&&&puts("Got a null pointer");311&&&&&&&&} else {312&&&&&&&&&&&&puts("Got a valid pointer");313&&&&&&&&}314&&&&&315&&&&这是一个有趣的问题。最近我的一个同事不经意把0值传给了函数malloc,得到了一个合法的指针之后,我才想到这个316问题。这就是上面的代码。该代码的输出是“Got a valid pointer”。我用这个来开始讨论这样的一问题,看看被面试者是317否想到库例程这样做是正确。得到正确的答案固然重要,但解决问题的方法和你做决定的基本原理更重要些。318&319typedef320&32115. typedef在C语言中频繁用以声明一个已经存在的数据类型的同义名称。也可以用预处理器做类似的事。例如,思考一下322下面的两个语句的区别:323&324&&&&#define dPS struct s *325&&&&typedef struct s * tPS;326&327以上两句的意图都是要定义dPS和tPS为一个指向结构s指针。哪种方法(如果有的话)更好呢?为什么?328&329&&&&这是一个非常微妙的问题,任何人答对这个问题都是值得祝贺的。答案是:typedef更好。比如下面的例子:330&331&&&&&&&&dPS p1,p2;332&&&&&&&&tPS p3,p4;333&334&&&&这样的话,第一个扩展为335&336&&&&&&&&struct s * p1, p2;&& // 编译器仅对#define指令作文本(字面上地)替换337&338上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。第二个例子正确地定义了p3和p4两个339指针。340&34116 . C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么?342&343&&&&int a = 5, b = 7,344&&&&c = a+++b;345&346&&&&这个问题将做为这次面试的一个愉快的结尾。不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理347它?水平不高的编程工作者经常会争论这个问题,而编译器应当能处理尽可能所有合法的用法。上面的代码可被处理成:348&349&&&&c = a++ +350&351因此, 这段代码持行后,a = 6, b = 7, c = 12。352&&&&如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处353是这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题。354&355&356&&&&好了,伙计们,你现在已经做完所有的测试了。这就是我出的C语言测试题,我怀着愉快的心情写完它,希望你以同样357的心情读完它。如果你认为这是一个好的测试,那么请你多加学习。358&&&&Nigel Jones是一个顾问,现在住在Maryland,当他不在水下时,你能在多个领域的嵌入项目中找到他。他很高兴能收到359读者的来信,他的email地址是: 360&361&362&363References:364o; [1]Jones, Nigel, "In Praise of the #error directive",Embedded Systems Programming, September 1999, p. 114.365o; [2]Jones, Nigel, "Efficient C Code for Eight-bit MCUs",Embedded Systems Programming, November 1998, p. 66.366&367&368******************************************************************************************************369**&&&&&&&&&&&&&&&&&&&&&&&&&&& END370******************************************************************************************************371*/
作者的其他最新博客
评论 ( 个评论)
Powered by1、嵌入式系统中经常要用到无限循环,你怎么样用C;while(1){};一些程序员更喜欢如下方案:;for(;;){};这个实现方式让我为难,因为这个语法没有确切表达到;基本原理;第三个方案是用goto;Loop:;...;gotoL;应试者如给出上面的方案,这说明或者他是一个汇编语;2、关键字volatile有什么含意并给出三个不;一个定义为vol
1、嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢? 这个问题用几个解决方案。我首选的方案是:
while(1) { }
一些程序员更喜欢如下方案:
for(;;) { }
这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的
基本原理。如果他们的基本答案是:“我被教着这样做,但从没有想到过为什么。”这会给我留下一个坏印象。
第三个方案是用 goto
应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。
2、关键字volatile有什么含意并给出三个不同的例子。
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。 假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。
1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数有什么错误:
int square(volatile int *ptr)
{ return *ptr * *
} 下面是答案:
1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
3). 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码: int square(volatile int *ptr)
{ int a,b;
return a *
} 由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int *ptr)
return a *
3、嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。
对这个问题有三种基本的反应
1). 不知道如何下手。该被面者从没做过任何嵌入式系统的工作。
2). 用bit fields。Bit fields是被扔到C语言死角的东西,它保证你的代码在不同编译器之间是不可移植的,同时也保证了的你的代码是不可重用的。我最近不幸看到 Infineon为其较复杂的通信芯片写的驱动程序,它用到了bit
fields因此完全对我无用,因为我的编译器用其它的方式来实现bit fields的。从道德讲:永远不要让一个非嵌入式的家伙粘实际硬件的边。
3). 用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法,是应该被用到的方法。最佳的解决方案如下:
#define BIT3 (0x1&&3)
void set_bit3(void)
{ a |= BIT3;
} void clear_bit3(void)
{ a &= ~BIT3;
} 一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的。我希望看到几个要点:说明常数、|=和&=~操作。
4、嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。
这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换
(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下:
ptr = (int *)0x67a9;
*ptr = 0xaa55;
一个较晦涩的方法是:
*(int * const)(0x67a9) = 0xaa55;
即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。
5、中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展―让标准C支持中断。具代表事实是,产生了一个新的关键字 __interrupt。
下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。
__interrupt double compute_area (double radius)
{ double area = PI * radius *
printf(& Area = %f&, area);
这个函数有太多的错误了,以至让人不知从何说起了:
1). ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。
2). ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。
3). 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。
4). 与第三点一脉相承,printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。
6、尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(heap)中动态分配内存的过程的。那么嵌入式系统中,动态分配内存可能发生的问题是什么? 这里,我期望应试者能提到内存碎片,碎片收集的问题,变量的持行时间等等。这个主题已经在ESP杂志中被广泛地讨论过了(主要是 P.J. Plauger, 他的解释远远超过我这里能提到的任何解释),所有回过头看一下这些杂志吧!让应试者进入一种虚假的安全感觉后,我拿出这么一个小节目:下面的代码片段的输出是什么,为什么?
if ((ptr = (char *)malloc(0)) == NULL)
puts(&Got a null pointer&);
puts(&Got a valid pointer&);
这是一个有趣的问题。最近在我的一个同事不经意把0值传给了函数malloc,得到了一个合法的指针之后,我才想到这个问题。这就是上面的代码,该代码的输出是“Got a valid pointer”。我用这个来开始讨论这样的一问题,看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要,但解决问题的方法和你做决定的基本原理更重要些。
包含各类专业文献、生活休闲娱乐、高等教育、92几道常见的嵌入式开发C语言面试题等内容。 
 嵌入式C语言面试题_电子/电路_工程科技_专业资料。1、用预处理指令#define ...9.ARM 有哪几个异常类型? (1)复位异常 -& SVC 模式 [开发板复位] (2)...  嵌入式开发面试题2 10页 免费 16道嵌入式C语言面试题 9页 免费如要投诉违规内容,请到百度文库投诉中心;如要提出功能问题或意见建议,请点击此处进行反馈。 ...  嵌入式Linux工程师常见笔试题_计算机软件及应用_IT/...17.请描述 Linux 下程序开发到执行的工作流程(结合...4、 C 语言的左值(lalue)和右值(rvalue)的含义是...  嵌入式C语言运用(笔试面试... 8页 2财富值 嵌入式开发--C语言面试 12页 2...(d) 以上均不是 第 4 题:考查指针,这道题只适合于那些特别细心且对指针和...  嵌入式程序员C语言笔试经典题_求职/面试_求职/职场...意味着常数&,我就知道我正在和一个业余者打交道。...这导致了很多编译开发商提供一种扩展―让标准 C ...  百度文库上所有C语言面试题的整理总汇,从基础面试题...RTOS 等等打交道,所有这些都要求 用到 volatile ...尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从...  嵌入式C语言考试试卷_工学_高等教育_教育专区。6、 在差错处理时, 忘了释放已分配的动态存储空间, 容易造成内存泄露。 (√) 一、填空题 20% 1、定义 int**...  嵌入式C语言面试题_计算机软件及应用_IT/计算机_专业资料。C语言文档...嵌入式开发―C语言面试题... 18页 免费喜欢此文档的还喜欢 16道嵌入式C语言...  简述 C 函数:1)参数如何传递(__cdecl 调用方式) ;2)返回值如何传递;3)调用...国家开发银行2014校园招... 32页 免费
2014大疆嵌入式笔试题 2页 免费 ©...

我要回帖

更多关于 嵌入式系统 的文章

 

随机推荐