免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 897 | 回复: 0
打印 上一主题 下一主题

嵌入式程序员应该知道的16个问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-12-07 00:03 |只看该作者 |倒序浏览
1、预处理器(Preprocessor)... 1
2、如何定义宏...
2
3、预处理器标识#error的目的是什么?... 4
4、死循环(Infinite loops)... 4
5、数据声明(Data
declarations)... 5
6、关键字static的作用是什么?... 6
7、关键字const有什么含意?...
7
8、Volatile的使用... 9
9、位操作(Bit manipulation)...
12
10、访问固定的内存位置(Accessing fixed memory locations)...
13
11、中断(Interrupts)... 13
12、符号扩展的代码例子(Code examples)...
15
13、处理器字长导致的数据扩展问题... 16
14、动态内存分配(Dynamic memory allocation)...
16
15、用Typedef构造复合类型... 17
16、晦涩的语法及代码风格...
18
C语言测试是招聘嵌入式系统程序员过程中必须而且有效的方法。这些年,我既参加也组织了许多这种测试,在这过程中我意识到这些测试能为面试者和被面试者提供许多有用信息,此外,撇开面试的压力不谈,这种测试也是相当有趣的。
从被面试者的角度来讲,你能了解许多关于出题者或监考者的情况。这个测试只是出题者为显示其对ANSI标准细节的知识而不是技术技巧而设计吗?这是个愚蠢的问题吗?如要你答出某个字符的ASCII值。这些问题着重考察你的系统调用和内存分配策略方面的能力吗?这标志着出题者也许花时间在微机上而不是在嵌入式系统上。如果上述任何问题的答案是"是"的话,那么我知道我得认真考虑我是否应该去做这份工作。
从面试者的角度来讲,一个测试也许能从多方面揭示应试者的素质:最基本的,你能了解应试者C语言的水平。不管怎么样,看一下这人如何回答他不会的问题也是满有趣。应试者是以好的直觉做出明智的选择,还是只是瞎蒙呢?当应试者在某个问题上卡住时是找借口呢,还是表现出对问题的真正的好奇心,把这看成学习的机会呢?我发现这些信息与他们的测试成绩一样有用。
有了这些想法,我决定出一些真正针对嵌入式系统的考题,希望这些令人头痛的考题能给正在找工作的人一点帮助。这些问题都是我这些年实际碰到的。其中有些题很难,但它们应该都能给你一点启迪。
这个测试适于不同水平的应试者,大多数初级水平的应试者的成绩会很差,经验丰富的程序员应该有很好的成绩。为了让你能自己决定某些问题的偏好,每个问题没有分配分数,如果选择这些考题为你所用,请自行按你的意思分配分数。
1、预处理器(Preprocessor)
用预处理指令#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)  6) ? puts("> 6") : puts("6"。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于频繁用到无符号数据类型的嵌入式系统(硬件寄存器的值全部是无符号的)来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。
13、处理器字长导致的数据扩展问题
评价下面的代码片断:
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1's complement of zero */
0的补码为全1的数
对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下:
unsigned int
compzero = ~0;
这一问题真正能揭露出应试者是否懂得处理器字长的重要性(嵌入式平台可能是8、16、32的,移植的角度来说写出固定的0xFFFF是不对的)。在我的经验里,好的嵌入式程序员非常准确地明白硬件的细节和它的局限,然而PC机程序往往把硬件作为一个无法避免的烦恼。
到了这个阶段,应试者或者完全垂头丧气了或者信心满满志在必得。如果显然应试者不是很好,那么这个测试就在这里结束了。但如果显然应试者做得不错,那么我就扔出下面的追加问题,这些问题是比较难的,我想仅仅非常优秀的应试者能做得不错。提出这些问题,我希望更多看到应试者应付问题的方法(很重要哦,面试者关注的是你思考问题解决问题的过程,当你不知道答案时千万千万不要猜一个答案给他,因为现在不是选择题,面试官要的是过程,你只需要将你考虑问题的过程说明白就OK了),而不是答案。不管如何,你就当是这个娱乐吧...
14、动态内存分配(Dynamic memory allocation)
尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(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"。我用这个来开始讨论这样的一问题,看看被面试者是否想到库例程这样做是正确(因为如果申请失败,则程序处理认为内存不足了,一般会终止程序,是很严重的问题?)。得到正确的答案固然重要,但解决问题的方法和你做决定的基本原理更重要些。
返回一個控指針還是指向 0
字節的指針甚至指向一个可以操作的指针?
(取决于系统平台的实现,C99及其他标准规定可以不同的)
malloc(0) in
glibc returns a valid pointer to something(!?!?) while in uClibc calling
malloc(0) returns a NULL. The behavior of malloc(0) is listed as
implementation-defined by SuSv3, so both libraries are equally correct. This
difference also applies to realloc(NULL, 0). I personally feel glibc's behavior
is not particularly safe. To enable glibc behavior, one has to explicitly enable
the MALLOC_GLIBC_COMPAT option.
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。
如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处是这是一个关于代码编写风格(要明确的加上括号,避免歧义或者编译器不同带来的差异),代码的可读性,代码的可修改性的好的话题。
注:引出代码风格的问题正是作者问此问题的目的,这告诉我们要揣摩面试管每个问题背后隐藏的考查点,能够趁机发挥下就大功告成了!
好了,伙计们,你现在已经做完所有的测试了。这就是我出的C语言测试题,我怀着愉快的心情写完它,希望你以同样的心情读完它。如果是认为这是一个好的测试,那么尽量都用到你的找工作的过程中去吧。天知道也许过个一两年,我就不做现在的工作,也需要找一个。
以下为上述16个问题的英文表述,熟悉下相关的专业词汇对于英文面试的简单表述很重要。
[color="#0000ff"]http://www.yuanma.org/data/2007/0509/article_2585.htm
An
obligatory and significant part of the recruitment process for embedded systems
programmers seems to be the "C test." Over the years, I have had to both take
and prepare such tests and, in doing so, have realized that these tests can be
informative for both the interviewer and interviewee. Furthermore, when given
outside the pressure of an interview situation, these tests can also be quite
entertaining.
From the interviewee's perspective, you can learn a lot
about the person who has written or administered the test. Is the test designed
to show off the writer's knowledge of the minutiae of the ANSI standard rather
than to test practical know-how? Does it test ludicrous knowledge, such as the
ASCII values of certain characters? Are the questions heavily slanted towards
your knowledge of system calls and memory allocation strategies, indicating that
the writer may spend his time programming computers instead of embedded systems?
If any of these are true, then I know I would seriously doubt whether I want the
job in question.
From the interviewer's perspective, a test can reveal
several things about the candidate. Primarily, you can determine the level of
the candidate's knowledge of C. However, it's also interesting to see how the
person responds to questions to which they don't know the answers. Do they make
intelligent choices backed up with good intuition, or do they just guess? Are
they defensive when they are stumped, or do they exhibit a real curiosity about
the problem and see it as an opportunity to learn something? I find this
information as useful as their raw performance on the test.
With these
ideas in mind, I have attempted to construct a test that is heavily slanted
towards the requirements of embedded systems. This is a lousy test to give to
someone seeking a job writing compilers! The questions are almost all drawn from
situations I have encountered over the years. Some of them are tough; however,
they should all be informative.
This test may be given to a wide range
of candidates. Most entry-level applicants will do poorly on this test, while
seasoned veterans should do very well. Points are not assigned to each question,
as this tends to arbitrarily weight certain questions. However, if you choose to
adapt this test for your own uses, feel free to assign scores.
Preprocessor
1. Using the #define statement, how would you
declare a manifest constant that returns the number of seconds in a year?
Disregard leap years in your answer.
#define SECONDS_PER_YEAR
(60 * 60 * 24 * 365)UL
I'm looking for several things
here:
Basic knowledge of the #define syntax (for example, no semi-colon
at the end, the need to parenthesize, and so on)
An understanding that
the pre-processor will evaluate constant expressions for you. Thus, it is
clearer, and penalty-free, to spell out how you are calculating the number of
seconds in a year, rather than actually doing the calculation yourself
A
realization that the expression will overflow an integer argument on a 16-bit
machine-hence the need for the L, telling the compiler to treat the variable as
a Long
As a bonus, if you modified the expression with a UL (indicating
unsigned long), then you are off to a great start. And remember, first
impressions count!
2. Write the "standard" MIN macro-that is, a
macro that takes two arguments and returns the smaller of the two arguments.
#define MIN(A,B)
((A)
6) ? puts("> 6") :
puts("
6."
The reason for this is that expressions involving signed and unsigned types have
all operands promoted to unsigned types. Thus ?20 becomes a very large positive
integer and the expression evaluates to greater than 6. This is a very important
point in embedded systems where unsigned data types should be used frequently
(see Reference 2). If you get this one wrong, you are perilously close to not
getting the job.
13. Comment on the following code fragment.
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1's complement of zero */
On machines where an int is
not 16 bits, this will be incorrect. It should be coded:
unsigned int
compzero = ~0;
This question really gets to whether the candidate
understands the importance of word length on a computer. In my experience, good
embedded programmers are critically aware of the underlying hardware and its
limitations, whereas computer programmers tend to dismiss the hardware as a
necessary annoyance.
By this stage, candidates are either completely
demoralized-or they're on a roll and having a good time. If it's obvious that
the candidate isn't very good, then the test is terminated at this point.
However, if the candidate is doing well, then I throw in these supplemental
questions. These questions are hard, and I expect that only the very best
candidates will do well on them. In posing these questions, I'm looking more at
the way the candidate tackles the problems, rather than the answers. Anyway,
have fun...
Dynamic memory allocation
14. Although not as
common as in non-embedded computers, embedded systems do still dynamically
allocate memory from the heap. What are the problems with dynamic memory
allocation in embedded systems?
Here, I expect the user to mention
memory fragmentation, problems with garbage collection, variable execution time,
and so on. This topic has been covered extensively in ESP , mainly by P.J.
Plauger. His explanations are far more insightful than anything I could offer
here, so go and read those back issues! Having lulled the candidate into a sense
of false security, I then offer up this tidbit:
What does the following
code fragment output and why?
char *ptr;
if ((ptr = (char
*)malloc(0)) ==
NULL)
else
puts("Got a null
pointer");
puts("Got a valid pointer");
This is a fun
question. I stumbled across this only recently when a colleague of mine
inadvertently passed a value of 0 to malloc and got back a valid pointer! That
is, the above code will output "Got a valid pointer." I use this to start a
discussion on whether the interviewee thinks this is the correct thing for the
library routine to do. Getting the right answer here is not nearly as important
as the way you approach the problem and the rationale for your decision.
Typedef
15. Typedef is frequently used in C to declare
synonyms for pre-existing data types. It is also possible to use the
preprocessor to do something similar. For instance, consider the following code
fragment:
#define dPS struct s *
typedef struct s *
tPS;
The intent in both cases is to define dPS and tPS to be
pointers to structure s. Which method, if any, is preferred and why?
This is a very subtle question, and anyone who gets it right (for the
right reason) is to be congratulated or condemned ("get a life" springs to
mind). The answer is the typedef is preferred. Consider the declarations:
dPS p1,p2;
tPS p3,p4;
The first expands to:
struct s * p1, p2;
which defines p1 to be a pointer to
the structure and p2 to be an actual structure, which is probably not what you
wanted. The second example correctly defines p3 and p4 to be pointers.
Obscure syntax
16. C allows some appalling constructs. Is
this construct legal, and if so what does this code do?
int a =
5, b = 7, c;
c = a+++b;
This question is intended to be a
lighthearted end to the quiz, as, believe it or not, this is perfectly legal
syntax. The question is how does the compiler treat it? Those poor compiler
writers actually debated this issue, and came up with the "maximum munch" rule,
which stipulates that the compiler should bite off as big (and legal) a chunk as
it can. Hence, this code is treated as:
c = a++ + b;
Thus, after
this code is executed, a = 6, b = 7, and c = 12.
If you knew the answer,
or guessed correctly, well done. If you didn't know the answer then I wouldn't
consider this to be a problem. I find the greatest benefit of this question is
that it is good for stimulating questions on coding styles, the value of code
reviews, and the benefits of using lint.
Well folks, there you have it.
That was my version of the C test. I hope you had as much fun taking it as I had
writing it. If you think the test is a good test, then by all means use it in
your recruitment. Who knows, I may get lucky in a year or two and end up being
on the receiving end of my own work.
Nigel Jones is a consultant living
in Maryland. When not underwater, he can be found slaving away on a diverse
range of embedded projects. He enjoys hearing from readers and can be reached at
NAJones@compuserve.com .
References
Jones, Nigel, "In Praise of
the #error directive," Embedded Systems Programming, September 1999, p. 114.
Jones, Nigel, " Efficient C Code for Eight-bit MCUs ," Embedded Systems
Programming, November 1998, p. 66.
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/107452/showart_2112437.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP