学习方法, 心态 和就业感言 - 上海信约科技嵌入式开发培训中心 0901期学员

分享一下我的收获:学习方法和心态 - 上海信约科技嵌入式开发培训中心 0901期学员 秦同学 

     四个月时间,不算短暂,然而贯穿着紧张和忙碌的学习,让人感觉过的飞快。四个月的收获还是很多的,尤其是对Linux平台的熟悉和对嵌入式行业的理解上,同时期间也养成了一些很好的开发习惯。总结整个学习过程,我想从学习方法和心态两个方面和大家分享一些个人收获。

     首先我想说的是基础的重要性,固然买本n小时精通某某可以让你很快进入开发,但是当你遇到问题的时候会发现自己无从下手。C是一门经典的编程语言,花时间深入的学习是很值得的,读一读标准库或者是一些优秀开源项目的源码是很好的一个途径。学习之初可以先从模仿开始,然后可以去理解他人的开发思路;首先可以从功能的实现入手,然后可以分别从效率和可读性的角度进行优化。通过更多的阅读和实践,巩固自己的基础。

     在Linux的学习上,除了大量的实践,更多的需要从操作系统本身去理解。工欲善其事,必先利其器。熟练使用Linux平台的大量GNU工具可以大大提升自己的开发效率,对于一些常用的服务,Shell编程花些学习和理解很有帮助。期间一些开发良好的习惯的培养也是不无裨益的。

     对于求职来说有很多技巧,我想说的话题是关于心态。这个过程本来就是要花时间的,所以没有必要过于着急,平稳的心态非常重要,及时的准备和总结不只对当前,对以后的求职都是很好的经验,毕竟这个过程不是经常遇到。

    在这里,祝愿所有同学都能找到自己的方向,在以后的职业道路上走的稳健和愉快。

上海信约科技嵌入式开发培训中心 0901期学员 秦同学





我的就业感言:坚持,永不放弃 - 上海信约科技嵌入式开发培训中心 0901期学员 孙同学 

                                     ——上海信约科技嵌入式开发培训中心学员就业感言

     时间过的真快,转眼我已工作一个月有余了,因为刚上班,有很多需要提升和适应的地方,所以一直很忙碌。现在回忆起四个多月在信约的学习时光,真的让我学到了很多。

     毕业之后,我找工作并不太顺利,可能是我毕业一年多,并没有任何工作经历的原因,而且专业技能掌握的也不是很好吧。虽有面试机会,但一直没有拿到offer,可是我没有放弃,一直坚持复习并找自己的原因,老师也帮助我分析和总结,并不断鼓励我,在我们共同坚持努力下,最后终于走上了工作岗位。现在说说我在找工作过程中的体会,希望能给大家一些参考。

学习阶段:

    1、 c语言很关键,好多企业考试题都有C语言,所以一定要把基础打扎实,因为四个月的时间短,学习的内容多,因此要自己多挤时间。多复习多做笔试题。

    2、 如果你想做应用和系统开发,那linux 操作系统,网络编程,TCP/IP协议,脚本等要多练。

    3、 另外,我要说说项目,在我面试过程中,我感觉项目真的很重要,因为好多企业看重的还是我们的逻辑思维,编程思路等,所以整个项目过程中,真的要认真去做,严格要求自己。

面试阶段:

    1、 面试前期一定要做好充分的准备工作,就业老师会为大家培训,并有一些技巧和经验,关键看自己会不会灵活运用和面试技巧的把握。

    2、 技术部门面试时,一定要对自己有信心,尽量发挥自己的专业水平,强调自己的学习能力和解决问题能力。

    3、 最后,我要说说心态。在找工作过程中,是一个提升自我的经历,所以一定要有良好的心态,不要放弃,要坚持,多总结,加强自己不足的地方。

     以上是我找工作过程中的一些体会和心得,我在此祝大家一切顺利,事业有成。因为我相信,只要坚持不懈,把握机会,相信大家一定会找到理想的工作的。

上海信约科技嵌入式开发培训中心 0901期学员 孙同学




一个低学历者的辛酸程序路 - 轉載

   2000盛夏的某个下午,我躺在河边看武侠小说,旁边放着鱼竿, 因为实在无聊透顶。突然妈找来了,告诉我哥从北京打来电话, 让我过去上班。

    我97年高中毕业未高考,后在外面帮人做装修,一年2000块钱,没 有休息,吃住都在一个小门脸里面,三年下来,我没有乱花钱, 还是两手空空,还被别人讥笑为“木讷”,“傻”,意思是不油滑。 不会黑顾客。虽然这样,我满手是伤,被玻璃划,被裁纸刀 划,冬天无法戴手套干活被冻裂。再次回到家里的时候,我大哭 一场感觉走投无路,最后一丝闯趟的想法也没有了。父亲安 慰我说,以后你一头牛,我一头牛,就当个牛倌吧。

    哥的电话重新唤起了我对未来美好生活的向往。北京,以前只 在电视上看到过,还有梦见过的天安门,五星红旗。

    哥自小就很聪明,一直第一第一的到了中国的最高学府,那时候 他还本科没毕业,托一个同学在海淀硅谷电脑城给我找了一个学装机器的活。他很在乎我,他说就这么一个弟弟,不能让你在家荒废下去。初期的时候我就住在他们宿舍里面,他的同学对我很照顾,至今我都感激不已。

    学装机器简单点说就是一个搬运工,搬显示器,搬电脑,装货, 卸货,每天累得回宿舍就想睡觉。每个月600块不管吃住。电脑城每天吵吵闹闹,让我很茫然。更让我觉得郁闷的是我想学 Windows 98,同事都不乐意交我,让我觉得那是个很高深的活,这使得以后在工作中只要同事需要帮助,我一定帮忙。我太 能体会这种感觉了。

    两个月下来,我不但啥没学到,倒是对周边的人越来越向往, 我希望有一个书房,每天可以看看书;希望有台电脑,可以让我 彻底的把Windows98给弄熟练了;我更希望每天可以不用搬显示器,太多了,搬不完。终于有天崩溃了,背着我哥辞职了,搬到了上庄,一个月房租90块钱,还管水电。

    哥知道以后找过来,他有些生气,我告诉不想干这个。接着他让 我到他们实验室去打字,也许以后可以找个打字员的活。几年的体力活让我手指初大,一分钟TT就打了两个字母。哥有些绝望, 后来给我买了一台机器,装好TC和TT,还有五笔。我又回到了上庄,那时候正下雪,到处白茫茫一片,我就像抱着宝物迷失在 旷野的小P孩。

    那些日子是孤独而又快乐的,每天不用干活,饿了自己吵个土豆片,空虚的时候看看书,周末的时候按照哥说的,吃个肉,补充营养。我很快的将Windows98搞得滚瓜烂熟,字也打得比以前快 多了,TC也能写个Hello World了,拉下很久的英语也快速的赶上去了。但那时我基础依然很差,我高中文科都没读完,最差的是数学。当周围的自考生们觉得我很厉害的时,我也开始膨胀 了,觉得该出去活动了。

    接下来每天坐几个小时的车去找工作,饿着回来做饭,但依然没有 找到工作。很简单,我没有经验,也没有学历,更不擅于伪装。 看着那些写字楼里面穿着笔挺带着胸卡的先生小姐们,我无比的 羡慕,心想就是在如此干净凉快的环境中当个清洁工人也好啊。 一天一个自考的说我还很像学生,说你找工作就该说是大学毕业, 还怂恿我去办个假证。当时土豆都没了,他给我的建议让我就如 同黑夜中的一盏明灯,只要让我上班就可以,不想当一个废人。

    终于我说服我哥,办了个假证。那天取的时候,感觉自己像个小偷。凭着假证还有练了一段时间的计算机,我找了一个做网站 的活。记得那天一大堆人面试,当问到我的网页怎么做时,我 说这个简单,就掉口说怎么规划,把做什么搞清楚最重要。因为我不会做网页,但老板完了之后就跟我说你留下来。

    我终于可以上班了,是“观察”而不是搬显示器了。很长一段时间 沉浸在这种幸福种种,我应该算个白领了?那时工资只有800元, 应该什么领都不是。但终于可以潜下心来学习一点东西了,Drea mweaver和HTML一个星期下来也掌握了,那个星期我基本每天 只睡5个小时,因为要给老板看东西。当第一个静态网站做出来的 时候,我欢喜了好长一段时间,虽然那时候我已经22岁,虽然我 还不会任何脚本程序,但没有比做的东西被人认可更高兴的事了。

    某天哥给我演示了一个asp脚本,一个网页动态的显示不同样子让 我惊叹不已。他给了我一本ASP的书,我欢天喜地的拿去琢磨了。 看完哪本书我用了半年的时间,补充了数据库,vbscript,io等 各方面的知识,因为数学很差,一个简单的分页被我弄了一晚上才弄出来。也缺乏视野,不知道网上还有好多好多资料代码。

    我一直觉得自己不笨,数学差我就应该补回来,我从初中数学看起, 一直看完了大学数学,这得益于当时我很安静,还有做公司网站几乎没有什么活干。数学这个东西,你静下心来用心去思考,还是进 步很快的。期间查找数学资料的时间习惯了使用Google这个好东西, 习惯了看英文资料,最关键的是培养了独立思考的能力。我认为 人一旦具备了这个能力,以后要学什么东西就只是时间上的问题了。

    从这个公司离职的时候正是2004年春节,当时的工资也涨到了1800, 还有饭补,老板给钱等,也够养活自己了。但我觉得老是做网站,没有更好的机会来锻炼自己。现在想起来有些冒险,因为当时我也只会 asp,sqlserver。那年我也很顺利的通过了成人高考,去读纯粹为了拿个证。在几番面试之后,找了一个我认为的大公司,有200多人。 报道那天,我红着脸跟总监说没有学历,然后低着头等待,像等待审判 一样。片刻之后,总监告诉我“好好干活,我去说,不要让我失望!”。 当时公司用的Java和VC,我不明白总监怎么看上我。大概是冥冥之中 自会有你的一些机会,你努力了,就抓住了。

    Java是个好东西,彻底的改变了我对程序的看法,尤其是它开放的庞大代码库,还有数不尽的相关优秀关联库,就像一个潦倒半生的人掉进了宝藏。我以前的积累彻底的绽放了,我学会了Eclipse还能够做插件, 看其源代码,学会了Ant,学会了一些正规工程开发需要用的比如UML, CVS,Bug Track,Log,Unit Test等。初期我学Java买了一本“京京 工作室”的书之后,让我彻底的放弃了看中文技术书籍的想法,记得看 第一本影印Thinking In Java的时候,十分的痛苦,但坚持下来了, 好多英语过了四六级的人没有坚持下来,现在他们也只能看英文文档, 没法有耐心的系统看一本1000多页的书。人贵在坚持。当你看第一本 英文书完了之后,再看其它英文书的时候,就没那么难了,再看下去 字典也不用查了,当然也不用抓破头去断长句了。

    虽然在这个公司过的很顺心,但我依然明白自己的弱项,由于没有受过 正规计算机教育,理论很不杂实,如数据结构,算法,数据库设计等。 由于后来当上了一个小头目,对这些东西越来越没有时间关注了,成为 一个真正的程序员的想法从写第一个Hello World的时候就没有变过, 继续当小头目等待升迁还是换个安静的环境把这些东西补上呢?年龄 一天天变大,管理这个东西可以30岁以后开始,但30岁之后我还能学习那些与经验无关的理论吗?肯定是思维跟不上了。带着对总监的深深歉意,我离开了这家公司。2005年5月我离职的时候工资已经是 5500了。

    我很小心的寻找下一家公司,希望有个好的氛围和环境,肯定不能是做项目的。看了几个月书之后才找到一个学院派的公司,我同样告诉他们我没有学历,虽然还是有些说不出口,但不觉得可耻了。不管在 什么样的环境中,不管你来自哪里,为理想而奋斗是永恒的真理。

    我呆的部门是研发中心,部门有些比较厉害的人,以前在上一个公司 积累的一些狂妄很快被谦虚代替。我继续看算法导论,离散数学及其应用 ,设计模式,EJB以及一些软件设计方面的书,还学会了使用Mapple 来解决一些难以明白的数学公式。最关键的是学会了使用Amazon和Emule 来看电子书,这些书一般领先国内两年左右。一些数据结构算法的书 老是以c/c++来讲,没办法,只能把C++ Primer(en)看完了,连习题都 作完了花了3个多月时间,但不想继续深入,能写出来编译通过就 可以了。好多人问我为什么英文看得这么快,我都不想说了,看准正确 的方向,坚持下去----都是废话!

    2006年7月,我基本看完了我想要看的,感觉人都充实了许多,C++ STL和JDK里面的源代码,也能看懂一部分。谁再说我不懂算法, 我会微微一笑。不会再有愤怒与无奈了。

    我觉得国内作软件的公司普遍管理水平低下,计算机文化底蕴不足, 沿承下来的管理理念大部分就是“管人”。没有任何冲突和些许失望,我 还是决定走人,去到一些外企之类尝试一下。我今年26岁,还有机会。

    时常看到有些低学历同仁对未来很迷茫,也看到“大”龄人士总在怀疑 学计算机的可能性。我想对他们说,人要坚持首先要有信心。

    最喜欢汪峰那首“飞的更高”,如果你决定要飞,就一定要飞的更高。




写在大三的开始:学会取舍,学会独立学习 - 轉載

      大三了,大学中最宝贵最重要的一年。我选择以一次累却充实的嵌入式培训的结束作为大三的开始,来为大三的整个一年定下了基调:增强自己的技术与动手能力!同时,在大三的刚开始,很多人选择了考研,包括一些本以为会一起选择一个方向的兄弟们。我明白,大家都是在为梦想而努力,只是方式不同罢了。只不过,在以后的一段日子,在嵌入式这个方向,我可能就walk alone了

关于是否考研

    自从大学以来,总不喜欢随主流,主要觉得如果要是大学的主流对的话,那为什么还有那么多大学生就业很困难呢?于是,总搞非主流(话说我也是90后…囧):

    当很多人去到处旅游逛街的时候我比较喜欢去泡自习室、图书馆;

    当大一的时候,老五拉我去学Java的时候,我感觉学的人太多,竞争激烈而选择了门槛较高,涉及知识跨度比较大的嵌入式(很感谢老五一直以来能激励我,他现在的Java非常牛);

     当大二有些人选择图形界面编程的时候,我比较喜欢看数据结构,操作系统;

     当几乎所有人都不听王珏老师的软件工程时,我听得津津有味;

     综上所述,可以推出 :当很多人选择考研的时候,我选择去学技术,找工作,接着非主流~~O(∩_∩)O~~ 。

     其实关于是否考研的问题,在这个暑假也犹豫过,矛盾过,因为见识到了北京高校如此之优秀,资源如此之丰富,导师如此之厉害,让我很是心动。可我还是想把非常宝贵的时间用到我自己喜欢的事情上,每天早上醒来想到今天又能学到新东西,那份激情让我沉醉。在矛盾之际,和父亲通了一个多小时电话,当时的我几乎语无伦次,态度很摇摆,很彷徨,父亲就静静的听着,一直等到我平静下来,说:“其实一直让你考研也是想让你以后机会更多一些,但如果你真的喜欢这个东西,大学剩下的时间好好利用上,尽你最大的努力去做。”这对我来说是一份很重很重的承诺,因为家里的一些哥哥姐姐们都非常非常优秀,学历都很高,所以父亲一直以来都很想让我考研,没想到他会这样说,不再强迫我考研。一瞬间,我感觉一股极大的责任感油然而生,如果我不好好学技术,不更加刻苦怎能对得起这种父子间的承诺?Father, I will try my best to fight! 不再犹豫,不再摇摆,选择了就要坚持的走下去,十年如一日的坚持!还是杨云老师说得对,无论是考研还是做技术,大学期间一定要选择一个方向,坚持一直搞下去,等到从大学门出来才会有优势。因为只有长年累月的坚持,才能做到最棒。

为了这份承诺,我要更加努力!

关于嵌入式

     越是学嵌入式,越是发现嵌入式的门槛很高。尤其是想做系统工程师,必须对硬软件都很熟悉。对硬件熟悉了才明白芯片的各个管脚是什么用,才能操纵芯片中的寄存器来完成一些功能,这样才能写一些驱动程序来驱动硬件。对软件熟悉了才能提供给更上层更好的API接口,让应用层程序员能够更好的利用驱动。同时由于驱动是内核空间编程,对内核架构要有清晰地了解。最后,当需要一个操作系统的时候,还要进行移植,从构建交叉编译链到烧写bootloader,从编译内核到挂载文件系统,每一个都需要花大量的时间去熟练掌握。总之,嵌入式是一个需要大量积累的方向。

     而我现在最缺的就是时间,宝贵的时间!在北京培训的两个月只是跟着有丰富实战经验的老师将每一项都走了一遍,但仅是摸爬滚打的入门阶段,回去还需要花大量的时间去消化吸收再扩展强化,所以我决定下学期不去考软考了,相对而言还是嵌入式优先级更高一些,软考以后还有时间考,但不是现在,在这边学的嵌入式一旦没有得到好好吸收就亏大了,所以权衡了一下还是以嵌入式为重。

     昨天和老师交流了一会,老师的简单一句话改变了我很久以来的一种思维,我之前在走一种误区,夯实基础的本意是好的,但是方法不对,很多时候总是以为经常看书就能提高,但往往看完的书就很快忘记了,时间花费了,但是效率不高。牛牛猛老师说可以在实验中提高,快速上手,在实验中遇到不会的或者掌握不好的再进行针对性的看书或补充,这样提高很快,记得也牢固。Oh, My God! 一句话如醍醐灌顶,让我突然看到了一个新的境界(以前真是太傻了)。当晚思考了很久,关于到底该怎样学习,为什么很多课我们考完不久就还给了老师?为什么很多人说大学学的东西没用,学完都不记得了?原来我们都没用这些知识,知识只有明白为什么要学,并且能用到这些知识,我们才能更深入的去了解它,同时自己也会有使用知识去创造的成就感,来激发我们更深入的学习。

     也许,学以致用 -> 熟能生巧 -> 举一反三 -> 融会贯通,才是深入学习知识并牢固掌握的学习境界吧…




嵌入式工程师的经典面试题目 - 轉載

预处理器(Preprocessor)

1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

我在这想看到几件事情:

1) #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)

2) 懂得预处理器将为你计算常数表达式的值,因此直接写出你如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。

3) 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。

4) 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。

2 . 写一个标准宏MIN ,这个宏输入两个参数并返回较小的一个。

#define MIN(A,B) ((A) <= (B) ? (A) : (B))

这个测试是为下面的目的而设的:

1) 标识#define在宏中应用的基本知识。这是很重要的。因为在 嵌入(inline)操作符 变为标准C的一部分之前,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。

2) 三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优的代码,了解这个用法是很重要的。

3) 懂得在宏中小心地把参数用括号括起来

4) 我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事?

least = MIN(*p++, b);

3. 预处理器标识#error的目的是什么?

如果你不知道答案,请看参考文献1。这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录去找出象这种问题的答案。当然如果你不是在找一个书呆子,那么应试者最好希望自己不要知道答案。

死循环(Infinite loops)

4. 嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?
这个问题用几个解决方案。我首选的方案是:

while(1)
{
}

一些程序员更喜欢如下方案:

for(;;)
{
}

这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的基本原理。如果他们的基本答案是:我被教着这样做,但从没有想到过为什么。这会给我留下一个坏印象。

第三个方案是用 goto

Loop:
...
goto Loop;

应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。

数据声明(Data declarations)

5. 用变量a给出下面的定义

a) 一个整型数(An integer)

b)一个指向整型数的指针( A pointer to an integer)

c)一个指向指针的的指针,它指向的指针是指向一个整型数( A pointer to a pointer to an intege)r

d)一个有10个整型数的数组( An array of 10 integers)

e) 一个有10个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers to integers)

f) 一个指向有10个整型数数组的指针( A pointer to an array of 10 integers)

g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)

h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer )

答案是:

a) int a; // An integer

b) int *a; // A pointer to an integer

c) int **a; // A pointer to a pointer to an integer

d) int a[10]; // An array of 10 integers

e) int *a[10]; // An array of 10 pointers to integers

f) int (*a)[10]; // A pointer to an array of 10 integers

g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer

h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer

人们经常声称这里有几个问题是那种要翻一下书才能回答的问题,我同意这种说法。当我写这篇文章时,为了确定语法的正确性,我的确查了一下书。但是当我被面试的时候,我期望被问到这个问题(或者相近的问题)。因为在被面试的这段时间里,我确定我知道这个问题的答案。应试者如果不知道所有的答案(或至少大部分答案),那么也就没有为这次面试做准备,如果该面试者没有为这次面试做准备,那么他又能为什么出准备呢?

Static

6. 关键字static的作用是什么?

这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:

1) 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。

2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。

3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。

Const

7.关键字const有什么含意?

我只要一听到被面试者说:const意味着常数,我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着只读就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。)

如果应试者能正确回答这个问题,我将问他一个附加的问题:

下面的声明都是什么意思?

const int a;

int const a;

const int *a;

int * const a;

int const * a const;

/******/

前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字 const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由:

1) 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)

2) 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。

3) 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。

Volatile

8. 关键字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 * *ptr;
}

下面是答案:

1) 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

2) 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。

3) 这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:

int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}

由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:

long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}

位操作(Bit manipulation)

9. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量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)
static int a;

void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}

一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的。我希望看到几个要点:说明常数、|=和&=~操作。

访问固定的内存位置(Accessing fixed memory locations)

10. 嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。

这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下:

int *ptr;

ptr = (int *)0x67a9;

*ptr = 0xaa55;

A more obscure approach is:

一个较晦涩的方法是:

*(int * const)(0x67a9) = 0xaa55;

即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。

中断(Interrupts)

11. 中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字 __interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。

__interrupt double compute_area (double radius)
{
double area = PI * radius * radius;
printf(\nArea = %f, area);
return area;
}

这个函数有太多的错误了,以至让人不知从何说起了:

1) ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。

2) ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。

3) 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。

4) 与第三点一脉相承,printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。

代码例子(Code examples)

12 . 下面的代码输出是什么,为什么?

void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts(> 6) : puts(<= 6);
}

这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是 >6。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。

13. 评价下面的代码片断:

unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1''s complement of zero */

对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下:

unsigned int compzero = ~0;

这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里,好的嵌入式程序员非常准确地明白硬件的细节和它的局限,然而PC机程序往往把硬件作为一个无法避免的烦恼。

到了这个阶段,应试者或者完全垂头丧气了或者信心满满志在必得。如果显然应试者不是很好,那么这个测试就在这里结束了。但如果显然应试者做得不错,那么我就扔出下面的追加问题,这些问题是比较难的,我想仅仅非常优秀的应试者能做得不错。提出这些问题,我希望更多看到应试者应付问题的方法,而不是答案。不管如何,你就当是这个娱乐吧...

动态内存分配(Dynamic memory allocation)

14. 尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(heap)中动态分配内存的过程的。那么嵌入式系统中,动态分配内存可能发生的问题是什么?

这里,我期望应试者能提到内存碎片,碎片收集的问题,变量的持行时间等等。这个主题已经在ESP杂志中被广泛地讨论过了(主要是 P.J. Plauger, 他的解释远远超过我这里能提到的任何解释),所有回过头看一下这些杂志吧!让应试者进入一种虚假的安全感觉后,我拿出这么一个小节目:

下面的代码片段的输出是什么,为什么?

char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts(Got a null pointer);
else
puts(Got a valid pointer);

这是一个有趣的问题。最近在我的一个同事不经意把0值传给了函数malloc,得到了一个合法的指针之后,我才想到这个问题。这就是上面的代码,该代码的输出是Got a valid pointer。我用这个来开始讨论这样的一问题,看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要,但解决问题的方法和你做决定的基本原理更重要些。

Typedef

15 Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子:

#define dPS struct s *
typedef struct s * tPS;

以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。哪种方法更好呢?(如果有的话)为什么?

这是一个非常微妙的问题,任何人答对这个问题(正当的原因)是应当被恭喜的。答案是:typedef更好。思考下面的例子:

dPS p1,p2;
tPS p3,p4;

第一个扩展为

struct s * p1, p2;
.
上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。

晦涩的语法

16 . C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么?

int a = 5, b = 7, c;
c = a+++b;

这个问题将做为这个测验的一个愉快的结尾。不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?水平不高的编译作者实际上会争论这个问题,根据最处理原则,编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成:

c = a++ + b;

因此, 这段代码持行后a = 6, b = 7, c = 12。

如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处是这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题。

.END

http://www.xiajiashan.com/peixuninfo.asp?zbl=306

.END2

No comments:

Post a Comment