embedded software boot camp

A ‘C’ Test: The 0x10 Best Questions for Would-be Embedded Programmers (reprised)

Tuesday, September 15th, 2009 by Nigel Jones

In May 2000 Embedded Systems Programming magazine (now Embedded Systems Design) published an article I had written entitled “A ‘C’ Test: The 0x10 Best Questions for Would-be Embedded Programmers.”

A revised version is posted at: A ‘C’ Test: The 0x10 Best Questions for Would-be Embedded Programmers.

I received a lot of mail about it at the time (including a decent amount of hate mail), and, much to my amazement, I continue to get mail about it to this day. The article has been shamelessly copied all over the web and its title is a popular search term that drives people to this blog.

Be aware that this test has been widely publicized, so be very suspicious of someone that does really well on it! To illustrate my point, when I wrote the article I was doing some work at a large company and was sharing an office with a fellow consultant, Nelson. Naturally I had Nelson proof read the article. Fast forward a few months when Nelson went off to an interview with a new potential client. Well it so happens that the interview occurred on the same day that my article was published, and the interviewer proceeded to use it verbatim on Nelson. Nelson, of course, aced the test leaving the interviewer astounded. Needless to say, we both found this to be very amusing! Alas, I’ve never had anyone in the intervening 9+ years hit me with it. Maybe next week…

If you would like a version of this test in Word format, or could use some expertise from an embedded systems consultant, please contact me.

Tags:

16 Responses to “A ‘C’ Test: The 0x10 Best Questions for Would-be Embedded Programmers (reprised)”

  1. Peter Butler says:

    Your 0x10 best questions for would-be embedded programmers may overstate answer 6b. You say, “A variable declared static within a module … is accessible by all functions within that module.void f1(void) return ci+3;static int ci = 6;void f2(void) return ci+4;Will this compile? I would expect that ci would be unknown to f1(). In my understanding const vs. #define is mostly a scope issue.Your 0x10 best questions … is why I favor assembly code. I KNOW what is going on. No odd compiler or language strangeness. But to be fair I use C/C++ as appropriate. Consider fragments of code used to generate DTMF tones. I used (ARM Cortex-M3) assembly code to form (signed int) 2^24*sin(x) where x is an unsigned 32-bit integer = r*(2^32)/(2*pi) and r is in radians. I.e. x/(2^32) is the fraction of the way around a circle. I wanted 12-bit accuracy (any more would be wasted) and I needed abs(sin(x)) < 1.0 for all x. Truncating the Taylor series at x^7/7! gave me both speed and the desired accuracy.Code calling my assembly code was in C. I added appropriate constants to two variables, called my sin function twice and added the results. The result went into a software FIFO which in retrospect likely should have been written in C. The ISR playing the tone was in assembly. It could have been written in C but any processor change (even a different vendors Cortex-M3) would require changes. When it came time to write DTMF decode I used C and vendor-provided 32-bit software floating point. This was WAY easier than implementing the Goertzel algorithm in 8051 assembly.

  2. GregK says:

    HiNice job.I am thinking about other example to question 0x8. your example b is:"Non-stack variables referenced within an interrupt service routine."I think "Non-stack" is not appropriate. Memory allocated from stack do not differ specially than allocated during compile time. I am able image situation where thread allocate some memory on stack, pass pointer to this memory to IRQ, by global pointer, or signal from global queue. There are other methods, some RTOS using dispatcher to resolve/dispatch IRQ request, if so than routine can have argument or queue associated with IRQ to pass information. So IRQ eventually will using memory allocated from stack of other thread.What You think?

  3. Nigel Jones says:

    Peter:You are correct about the scope of static. That being said, anyone that does this deserves what will inevitably happen the first time the code gets refactored. Indeed you can take this a step further. I think it was reader 'Uhmmmmmm' that pointed out that one can indeed call a static function from another module (translation unit to be very precise), by simply calling it via a function pointer. Will this work? – sure, is it nuts? – yes.I find your statement about assembly language mostly true, but a strange justification for its use. (Note that I say this as someone who enjoys assembly language programming). For example, while it is in general true that with assembly language what you see is what you get,in my experience one soon starts resorting to higher level features in an assembler (such as parametrized macros, data structures, 'built-in' functions etc), and that these are even more language specific than 'standard' C.Now I am highly sympathetic to the use of assembly language for performance reasons. For example, I recently ranted about the lack of a 3 byte integer in most embedded C compilers that forces one to do perform operations on 4 byte integers when 3 bytes would do just fine. That being said, it's rare that I find myself in a CPU constrained situation, and so I find the need for assembly language to be diminishing. I might say that I think this is a bad thing, because until one has done assembly language programming, one can never really understand what is going on 'under the hood'.

  4. Nigel Jones says:

    GregK:To the best of my knowledge, the only variables allocated on the stack by a C compiler are function parameters and so called automatic variables (non statically allocated function variables declared at function scope). The key thing about these variables is that they have highly limited scope and also a highly limited lifetime. Now you can always abuse such variables by taking their address and passing this address to other code to dereference. I predict disaster if you do though.Incidentally, the example you quoted sounds to me more like a case where memory would be allocated from the heap.

  5. GregK says:

    HiOf course I am not going use such technique at all. It is not resist critique. It is just an example. I just wanted to discuss, it is rather academic discussion if it proper place. Probably not :), we are focusing on practical approaching.Of course disaster is close, but is in programmer responsibility to use this technique proper, and compiler responsibility to compliant with standard.But I am actually not sure of this code:void generic_thread(void * parm){ volatile data_t p; global_p = &p; /* is it possible to optimise and roll stack here????????? I doubt it can. we reference this variable below. but better do not relay on it. */ int tab[10]; for(;;) /* main loop */ { sleep(1); if (p.something) { } }}//However volatile in functions is really useful for debugging purpose under high optimization level. Very often you can not see value of variable under debugger. volatile (just in debug compilation) is better then write to global variable, since global variable could be optimized even though is not static (good compilers and linkers can do this).

  6. Darren says:

    I just thought I would point out a discussion about the original 2000 article currently generating a lot of traffic in the "Real-Time Embedded Engineering" group on LinkedIn. I can't see a static link so here is a link to the group and the discussion

  7. Nigel Jones says:

    Thanks Darren. I had noticed the traffic. I've applied to join the group and will attempt to contribute to the discussion. That being said I'm just about to head out on a business trip so my time may be limited in the very near future.

  8. Ian Johns says:

    > one can indeed call a static function from another module (translation unit to be very precise), by simply calling it via a function pointer. Will this work? – sure, is it nuts? – yes

    It isn’t nuts at all. For instance, an application could use a callback handler/manager that executes functions via pointers when certain configured events occur. A module may configure an event callback passing one of its own private/static functions. So although the static function may execute only when called via function pointer by the external callback handler, the static function may only be directly accessed/configured/etc. by its own module.

    This type of callback mechanism is very useful & usage with static functions is not unreasonable.

    • Gauthier says:

      I second that. You can have a module register a static function for running at a timer interrupt, as a more specific example.
      The function is not directly callable from outside, besides by the module it is registered to.

  9. Micro Engineer says:

    You know, I’ve been wanting to respond to this for quite some time.

    Unfortunately I found myself on the receiving end of this test, with the test being wielded more like a sword than a *partial* measure of aptitude and a subject’s interest in pursuing knowledge of embedded programming & the proper use of C for program design.

    It saddens me to know that otherwise “senior” interviewers should know better and should have learned what really matters in finding a suitable engineer. Ironically, some of those same interviewers could learn a thing or too about hardware-level abstraction, proper code documentation, and efficient code design.

    Now (after correcting any deficiencies admittedly identified from the test) I’d like to speak on what, from myexperience as an engineer, I believe truly is more important in “the big picture.”

    I cannot endorse any test as a SINGLE and TRUE measure of someone’s understanding. I can accept it being part of a larger group of interview questions & criteria used to better evaluate a candidate. And to help contribute to a bigger picture of who that person sitting in front of you is in terms of both technical knowledge & their interest in the field.

    For example, a test like this [by itself] does not address real-world issues like:

    – You have design problem “X”; how would you go about laying out the program logic and/or program design in general terms? (Logical state machines, flow charting w/ pseudo-code, etc)

    – Symptoms A, B, & C appear in a new design. Where & how would you suggest troubleshooting is approached, and using what tools? (Both hardware and software if need be)

    – Have you ever had to step through disassembled code and track down a bug?

    – Do you have code samples we can review? What is your personal policy regarding code documentation, portability/abstraction, and version control?

    – Engineer Joe is using the library function printfoo() but the result is a lot of ROM is already eaten up. What would be your approach to increasing more efficient use of the embedded device?

    – Do have any special projects, related accolades, etc you’d like to share? (Things like this demonstrate a lot-things you do voluntarily are a telling sign of a person’s interests and abilities!)

    Even such basic things really mean a lot when it comes down to the bottom line. Engineering is a hard field to work in some times, and it takes more than someone who can sloppily code type defines & pointers to get the job done.

    When one uses a test like this almost as the defacto, end-all hurdle to overcome, yet ignores a candidate’s zest for learning, awards, or work examples that demonstrate a desire to work in the field of their choice and benefit from continual self-improvement, one has no right to complain about not finding suitable candidates. Or if they move on to a company where their effort is properly accomodated.

    Nigel Jones mention himself in the test one goal which is to identify a candidate’s motivation to learn that which they don’t already know in addition to doing their best to be prepared. He also mentions facilitating discussion, not foregoing it or ignoring alternative perspectives to the design process. C syntax & the use of #define, etc. are only part of the equation.

    Food for thought!

  10. NickD says:

    Hi Nigel. I came across your 0x10 C questions through LinkedIn Embedded C group.

    This declaration (constant pointer to constant integer) on Q7 doesn’t seem correct?
    int const * a const; // compile error (gcc version 4.6.3)

    I’m not sure if the above declaration works ok on the compiler you used. I think this should be written as:
    int const * const a;
    or,
    const int * const a;

  11. Norman Diamond says:

    That is a very nice list of questions. They would lead to useful discussions. With that in mind, this comment is intended to be a useful discussion where competent people understand what we’re talking about but have different opinions on which answers are best. (Except for two disputes that are matters of fact instead of opinion.)

    Question 1.
    #define SECONDS_PER_YEAR (60UL * 60UL * 24UL * 365UL)
    “(c) An understanding that the pre-processor will evaluate constant expressions for you.”

    No it will not. If the preprocessor would evaluate constant expressions outside of places like the controlling constant-expression of an #if directive at preprocessing time, then you would not need the parentheses around 60UL * 60UL * 24UL * 365UL. You know why you need those parentheses and it is because the preprocessor does not evaluate that expression in most contexts. Later phases of translation will usually evaluate it as a constant-expression but sometimes they don’t have to do it either.

    “(e) As a bonus, if you modified the expression with a UL (indicating unsigned long), then you are off to a great start”

    I disagree. Very frequently there are reasons to make VARIABLES unsigned. Occasionally there are reasons to make CONSTANTS unsigned, so the suffix U does have a purpose, but this is not an appropriate example. The number of seconds per year does happen to be a positive number but that is not a reason to shove unsignedness onto every calculation that this number enters into. The number 3 is positive but you don’t always write that as 3U; if you have a signed int x and you want to multiply it by 3 you probably don’t want that 3 to be a 3U.

    Question 4.
    for(;;)
    “Personally, I dislike this construct because the syntax doesn’t exactly spell out what is going on.”

    Yes it does, and the inventors of the C language made it that way from day 1 for exactly that purpose. The loop won’t terminate until you break out of it by break, goto, return, etc.

    goto is useful in exceptional situations, but gratuitous use where a for(;;) would do fine only worsens clarity instead of improving.

    Question 7.
    “As soon as the interviewee says ‘const means constant’, I know I’m dealing with an amateur.”
    “If you haven’t been reading that column, suffice it to say that const means “read-only”. Although this answer doesn’t really do the subject justice, I’d accept it as a correct answer.”

    To me an answer of “read-only” is as amateurish as an answer of “constant”. In any case, when const is used in a declaration other than an actual definition of the object involved, it only buys one feature, though it is a useful feature: if you accidentally make a bug in code that would assign to the object where you did not intend to assign, the compiler will warn you at compilation time.

    “(b) const has the potential for generating tighter code by giving the optimizer some additional information.”

    I don’t think so. If the compiler can assume single threading and no interrupts, and your code doesn’t call any other functions … nope, that still isn’t enough. You’ll need newer features in order to tell a compiler that the object isn’t aliased.

    Question 9.
    “Bit fields are right up there with trigraphs as the most brain-dead portion of C.”

    That’s not really fair. C was invented as a substitute for assembly language, with SYNTAX that would allow programmers to be somewhat portable but not with a goal of letting programs port from one architecture to another (the inventors were surprised to discover that some degree of portability was actually accomplished). Programmers using a compiler would be expected to read the vendor’s documentation of how bitfields would be laid out.

    Trigraphs are an incredibly ugly syntax but they serve a needed purpose. If I write a program using some character strings needed by my colleagues or customers, you’ll probably get compilation errors because your compiler can’t handle Japanese strings. Similarly, I downloaded someone’s code and had to clean up where he used accented European characters which looked like lead bytes but didn’t form valid Japanese characters. In situations where C programs are intended to have some degree of portability, if the source code isn’t 7 bit clean then trigraphs have a purpose, ugly as they are. Of course better ways are known now.

    Question 14.
    “What are the problems with dynamic memory allocation in embedded systems?”

    I’d be stumped. I’m aware of lots of problems with dynamic memory allocation, but every problem that I’ve seen in an embedded system also arises in “ordinary” hosted systems. I’d interpret the question as asking what kind of problem arises in an embedded system that doesn’t arise in a hosted system, and I can’t really think of any.

    Question 15.
    #define dPS struct s *
    typedef struct s * tPS;
    “This is a very subtle question, and anyone that gets it right (for the right reason) is to be congratulated or condemned (“get a life” springs to mind).”

    I’d have thought this was a no-brainer and for anyone that gets it right the phrase “get a job” springs to mind (as in, anyone who uses dPS should not be looking for a job in C programming).

  12. Randall says:

    Most of the questions I see here being proposed are really just tricky (some of them) c programming questions. A great c programmer is not necessarily suitable as an embedded developer. What really needs to be asked, are questions specific to the software development challenges presented by embedded systems, especially CONSTRAINED embedded systems. Hear are some possibilities:

    1. What are some of the major differences between developing code on a PC in c vs. doing so on a constrained embedded system? Ans: The candidate should commnicate that both the codespace (eg. flash) and ram space is generally limited on a constrained embedded system vs. on a PC. This requires careful implementation of code to use as little ram as possible and to use it efficiently. Also code must be optimized for a small footprint.

    2. What is the relationship between power consumption and clock speed on a processor? Ans: directly proportional.

    3. Why is power consumption important for embedded systems and how might it be managed? Ans: Important when batteries are used by the system. Putting devices in standby mode when possible and selecting lowest possible clock speed.

    4. Discuss the ucontroller execution flow when an embedded system is powered on.. Ans: describes the power on reset flow.

    5. What is a watchdog timer and what is it’s use?

    6. Discuss some ways code can be optimized on an embedded system.

    7. Should an embedded system use an operating system? Discuss pros and cons. Discuss any examples you know of.

    8. What is debouncing and can you write some example code in c that shows how its done?

    9. Discuss the typical interrupts seen on an embedded system and how these are used.

    10. Discuss ways of debugging on an embedded system.

    11. What is JTAG?

    12. Write some c code to scan a keypad that has 16 keys orgainized in a 4 by 4 matrix. Give them a block diagram.

    13. How might you measure execution time of a critical block of code on an embedded system? Ans: have code toggle an IO port and connect it to an oscilloscope.

    14. What is a bootloader?

    15. On a constrained embedded system, are OO languages such as C++ or Java a “best” choice? Ans: Candidate should explain that OO languages will generally need more resources (ram and flash) than c. Thus they might not be appropriate.

    I can think of plenty more but you get the idea.

  13. Steve Iribarne says:

    I’ve used this test many times along with many of the suggestions in the comments fields.

    Thanks for the article.

Leave a Reply to Norman Diamond

You must be logged in to post a comment.