Interview Guide for Embedded C Programmers

Frequently asked questions in various embedded system interviews by the interviewer. So learn Embedded Systems with the help of this Interview Guide for Embedded C Programmers. Feel free to post your comments and remarks. These FAQ answered by Nigel Jones.

Preprocessor

1. Using the #define statement, how would you declare a manifest constant that return the number of seconds in a year? Disregard leap years in your answer.

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

The purpose of this question is to test the following:

  • Basic knowledge of #define syntax (for example, no semicolon at the end, the need to parenthesize, and so on).
  • An understanding that the Preprocessor will evaluate constant expression 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 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 great start. And remember, first impression counts.

learn-embedded-system

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)<=(B)?(A):(B))

The purpose of this question is to test the following:

  • Basic knowledge of the #define directive as used in macros. This is important because until the inline operator becomes part of standard C, macros are the only portable way of generating inline code. Inline code is often necessary in embedded systems in order to achieve the required performance level
  • Knowledge of the ternary conditional operator. This operator exists in C because it allows the compiler to produce more optimal code than an issue in embedded systems, knowledge and use of this construct is important
  • Understanding of the need to very carefully parenthesize arguments to macros
  • Some also use this question to start a discussion on the side effect of macros, for example, what happens when you write code such as:
    least = MIN(*P++, b);

3. What is the purpose of the preprocessor directive #error?

When the preprocessor hits the #error directive, it will report the string as an error message and halt compilation; what exactly the error message looks like depends on the compiler. This question is useful for differentiating between normal folks and the nerds. Only the nerds actually read the appendices of C textbooks to find out about such things.

Infinite loop

4. Infinite loops often arise in embedded systems. How does you code an infinite loop in C?

There are several solution to this question. My preferred solution is:

while(1)
{

}

Many programmers see to prefer:

for(;;)
{

}

This construct puzzles many people because the syntax doesn’t exactly spell out what’s going on. Thus, if a candidate gives this solution, one use it as an opportunity to explore their rationale for doing so. If their answer is basically, “I was taught to do it this way and I haven’t thought about it since, “It tells something (bad) about them.

A third solution is to use a goto:

Loop:
…..
goto Loop;

Candidates who propose this are either assembly language programmer (which is probably good), or else they are closet BASIC/FORTRAN programmers looking to get into a new field.

Data Declaration

5. Using the variable a, give definition for the following:

  • An integer
  • A pointer to an integer
  • A pointer to a pointer to an integer
  • An array of 10 integer
  • An array of 10 pointers to integers
  • A pointer to an array of 10 integers
  • A pointer to a function that takes an integer as an argument and returns an integer
  • An array of ten pointers to functions that take an integer argument and return an integer

The answers are:

int a;      // An integer
int *a;     // A pointer to an integer
int **a     // A pointer to a pointer to an integer
int a[10]   // An array of 10 integers
int *a[10]  // An array of ten pointers to integers
int (*a)[10] // A pointer to an array of 10 integers
int (*a)(int)// A pointer to a function a that takes an integer argument and returns an integer
int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer

Static

6. What are the uses of the keyword static?
This simple question is rarely answered completely. Static has three distinct uses in C:
A variable declared static within the body of a function maintains its value between function invocations. A variable declared static within a module, (but outside the body of a function) is accessible by all functions within that module. It is not accessible by functions within any other module. That is, it is localized global. Function declared static within a module may only be called by other functions within that module. That is, the scope of the function is localized to the module within which it is declared

Const

7. What does the keyword const mean?

As soon as the interviewee says “const means constant,” Everybody knows that he is dealing with amateur. Dan Saks has exhaustively covered const in the last year, such that every reader of ESP should be extremely familiar with what const can and cannot do for you. 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. (If you want the detailed answer, read saks’ columns-carefully!).

8. What do the following declarations mean?

const int a;
int const a;
const int *a;
int * const a;
int const * a const;

The first two means the same thing, namely a is a const (read-only) integer. The third means a is a pointer to a const integer (that is, the integer isn’t modifiable, but the pointer is). The fourth declares a to be a const pointer to an integer (that is, the integer pointed to by a is modifiable, but the pointer is not). The final declaration declares a to be a const pointer to a const integer (that is, neither the integer pointed to by a, nor the pointer itself maybe modified). If the candidate correctly answers these questions, we’ll be impressed. Incidentally, you might wonder why we put so much emphasis on const, since it is easy to write a correctly functioning program without ever using it. We have several reasons:

  • The use of const conveys some very useful information to someone reading your code. In effect, declaring a parameter const tells the user about its intended usage. If you spend a lot of time cleaning up the mess left by other people, you shall quickly learn to appreciate this extra piece of information.
  • Const has the potential for generating tighter code by giving the optimizer some additional information
  • Code that uses const liberally is inherently protected by compiler against inadvertent coding construct that result in parameters being changed that should not be. In short, they tend to have fewer bugs.

Volatile

9. What does the keyword volatile mean? Give three different examples of its use

A volatile variable is one that can change unexpectedly. Consequently, the compiler can make no assumptions about the value of the variable. In particular, the optimizer must be careful to reload the variable every time it is used instead of holding a copy in a register. Examples of volatile variables are:

  • Hardware registers in peripherals (for example, status registers)
  • Non-automatic variables referenced within an interrupt service routine
  • Variables shared by multiple tasks in a multi-threaded application

Candidates who don’t know the answer to this question aren’t hired. Most consider this fundamental question that distinguishes between C programmer and embedded systems programmer. Embedded folks deal with hardware, interrupt, RTOS, and the like. All of these require volatile variables. Failure to understand the concept volatile will lead to disaster.

On the (dubious) assumption that the interviewee gets this question correct, We like to probe a little deeper to see if they really understand the full significance of volatile. In particular, we’ll ask them the following additional questions:

  • Can a parameter be both const and volatile? Explain.
  • Can a pointer be volatile? Explain.
  • What’s wrong with the following function?
int square(volatile int *ptr)
{
  return *ptr * *ptr;
}

The answers are as follows:

  • Yes, an example is a read-only status register. It is volatile because it can change It is const because the program should not attempt to modify it.
  • Yes, although this is not very common. An example is when an interrupt service routine modifies a pointer to a buffer
  • This one is wicked. The intent of the code is to return the square of the value pointed to by *ptr. However, since *ptr points to a volatile parameter, the compiler will generate code that looks something like this:
int square(volatile int *ptr)
{
    int a, b;
    a = *ptr;
    b = *ptr;
    return a*b;
}

Because It’s possible for the value of *ptr to change unexpectedly, it is possible for a and b to be different. Consequently, this code could return a number that is not a square! The correct way to code this is

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

Bit Manipulation

10. Embedded System always requires the user to manipulate bits in registers or variables. Given an integer variable a, write two code fragments. The first should set bit 3 of a. The second should clear bit 3 of a. In both cases, the remaining bits should be unmodified.

These are the three basic responses to this question:

  • No idea. The interviewee cannot have done any embedded system work
  • Use bit fields. Bit fields are right up there with trigraphs as the most brain-dead portion of C. Bit fields are inherently non-portable across compilers, and as such     guarantee that your code is not reusable.
  • Use #define and bit masks. This is highly portable method and is the one that should be used. My optimal solution to this problem would be:
#define BIT3    (0x1<<3) static int a;
    
void set_bit3(void)
{
  a |= BIT3;
}
 
void clear_bit3(void)
{
  a &= ~BIT3;
}

Some people prefer to define a mask together with manifest constants for the set and clear values. This is also acceptable. The element that I’m looking for is the use of manifest constants, together with the |= and &= constructs.

Get Free Courses & Webinars
You'll receive only high quality learning material, tips & tricks
I agree to have my personal information transfered to MailChimp ( more information )
We respect your privacy

About Umesh Lokhande

Umesh Lokhande holds a Master degree in Scientific Instrumentation from University of Applied Sciences Jena, Germany. and has previously worked at Orbotech, Alere Technologies etc. Umesh is also a founder and first author of BINARYUPDATES.COM

Login

Register | Lost your password?