Preprocessor (Preprocessor)
1. Declare a constant with pre-processing instruction # define to indicate how many seconds are in 1 (ignoring leap year issues)
#define SECONDS_PER_YEAR (* * 365) UL
I want to see a few things here:
1) basic knowledge of #define grammar (for example: cannot end with semicolons, use of parentheses, etc.)
2) know that the preprocessor will calculate the value of the constant expression for you, so it is clearer and no cost to write directly how many seconds in a year instead of calculating the actual value.
3) realize that this expression will overflow the integer number of a 16-bit machine-so use the long integer symbol l to tell the compiler that this constant is a long integer number.
4) If you are using UL (for unsigned long integers) in your expression, then you have a good starting point. Remember, first impressions are important.
2. Write a "standard" macro min, this macro enters two parameters and returns the smaller one.
#define MIN (A) (A) <= (B)? (A): (B))
This test is designed for the following purposes:
1) identifies the basic knowledge that # define is applied in the macro. This is very important. Because macros are the only way to generate embedded code before the embedded (inline) operator becomes part of standard C, embedding code is often a necessary method for embedded systems to achieve the required performance.
2) Be careful to enclose the parameters in parentheses in the macro.
3) I also use this issue to start discussing the side effects of macros, such as: What happens when you write the following code?
least = MIN (*p++, b);
3. What is the purpose of the preprocessor identity #error?
If you don't know the answer, see reference 1. This question is useful for distinguishing between a normal guy and a nerd. Only nerds can read the appendix to the C language textbook to find answers like this. Of course, if you're not looking for a nerd, then it's best for a candidate to want to not know the answer.
Dead Loop (Infinite loops)
4. An infinite loop is often used in embedded systems, how do you write a dead loop in C?
This problem is solved with several solutions. My preferred solution is to:
while (1)
{
}
Some programmers prefer the following scenarios:
for (;;)
{
}
This implementation is embarrassing for me because the syntax doesn't exactly say what's going on. If a candidate gives this as a plan, I'll use this as an opportunity to explore the fundamentals of what they do. If their basic answer is: "I was taught to do this, but never thought of why." "It will leave a bad impression on me.
The third option is to use a goto
Loop:
...
Goto Loop;
If the candidate gives the above scenario, it means that either he is an assembly language programmer (which may be a good thing) or he is a Basic/fortran programmer who wants to enter a new field.
Data statement (declarations)
5. Use variable A to give the following definition
A) An integer number (an integer)
b) A pointer to the integer number (a pointer to an integer)
c) A pointer to a pointer to a pointer that points to a number of integers (a pointer to a pointer to an intege) r
d) An array of 10 integers (an arrays of ten integers)
e) An array of 10 pointers that point to an integer number. (An array of ten pointers to integers)
f) A pointer to the array of 10 integer numbers (a pointer to an array of ten integers)
g) A pointer to the function that has an integer parameter and returns an integer number (a pointer to a function this takes an integer as an argument and returns an integer)
h) An array of 10 pointers that point to a function that has an integer parameter and returns an integer number (an array of ten pointers to functions that taking an integer argument and ret Urn an integer)
The answer is:
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 ten integers
e) int *a[10]; An array of ten pointers to integers
f) Int (*a) [10]; A pointer to an array of ten integers
g) Int (*a) (int); A pointer to a function A, takes an integer argument and returns an integer
h) int (*a[10]) (int); An array of ten pointers to functions A, a integer argument and return an integer
I agree that there are a number of questions that people often claim are the kind of questions that need to be answered by flipping a book. When I wrote this article, I did check the book in order to determine the correctness of the grammar. But when I was interviewed, I expected to be asked the question (or similar question). Because during the interview, I was sure I knew the answer to the question. If the candidate does not know all the answers (or at least most of them), then he is not prepared for the interview, and if the interviewer does not prepare for the interview, then why should he be prepared?
Static
6. What is the role of the keyword static?
Few people can answer this simple question completely. In the C language, the keyword static has three distinct effects:
1) in the function body, a variable declared as static maintains its value in the process of the function being called.
2) within the module (but outside the function body), a variable declared as static can be accessed by a function within the module, but not by other functions outside the module. It is a local global variable.
3) within the module, a function declared as static can only be called by other functions within this module. That is, this function is restricted to the local scope of the module that declares it.
Most candidates are able to answer the first part correctly, and part of them can answer the second part correctly, with very few people able to understand the third part. This is a serious drawback for a candidate because he clearly does not understand the benefits and importance of localizing data and code scope.
Const
7. What is the meaning of the keyword const?
As soon as I heard the interviewee say "Const means constant", I knew I was dealing with an amateur. Last year Dan Saks has completely summed up all the uses of const in his article, so every reader of ESP (translator: Embedded Systems programming) should be very familiar with what const can and cannot do. If you never read that article, As long as you can say that const means "read-only" on it. Although this answer is not a complete answer, I accept it as a correct answer. (If you want to know more about the answer, read Saks's article carefully.) )
If the candidate can answer the question correctly, I will ask him an additional question:
What does the following statement mean?
const int A;
int const A;
const int *a;
int * const A;
int const * a const;
/******/
The first two functions are the same, a is a constant integer number. The third means that a is a pointer to a constant integer number (that is, the integer number is not modifiable, but the pointer can). The fourth meaning a is a constant pointer to an integer number (that is, the integer number pointed to by the pointer can be modified, but the pointer is non-modifiable). The last one means that a is a constant pointer to a constant number (that is, the integer number pointed to by the pointer is not modifiable and the pointer is not modifiable). If the candidate can answer these questions correctly, then he leaves a good impression on me. Incidentally, perhaps you may ask, even without the keyword const, it is easy to write the function of the correct program, then why do I value the keyword const? I also have the following several reasons:
1) The function of the keyword const is to convey very useful information to the person who is reading your code, and in fact, declaring a parameter as a constant is to tell the user the purpose of the application of this parameter. If you have spent a lot of time cleaning up the rubbish left by others, you will soon learn to thank for the extra information. (Of course, it's very rare for a const programmer to leave rubbish for others to clean up.) )
2) using the keyword const may produce more compact code by giving the optimizer some additional information.
3) The use of the keyword const makes it natural for the compiler to protect those parameters that you do not want to change, and to prevent them from being unintentionally modified. In short, this can reduce the occurrence of bugs.
Volatile
8. What does the keyword volatile mean? and gives three different examples.
A variable that is defined as volatile means that the variable may be unexpectedly changed so that the compiler does not assume the value of the variable. Precisely, the optimizer must carefully re-read the value of the variable each time it uses the variable, rather than using the backup stored in the register. Here are a few examples of volatile variables:
1) Hardware registers for parallel devices (e.g., status registers)
2) non-automatic variables that are accessed in an interrupt service subroutine (non-automatic variables)
3) variables shared by several tasks in multi-threaded applications
The person who cannot answer the question will not be hired. I think this is the most basic problem of distinguishing between C programmers and embedded system programmers. Embedded guys often deal with hardware, interrupts, RTOs, and so on, all of which require volatile variables. The content that does not understand volatile will bring disaster.
Assuming that the interviewee has answered the question correctly (well, wondering if this is the case), I'll look at it a little bit and see if this guy is straight on about the importance of volatile.
1) can a parameter be either const or volatile? explain why.
2); Can a pointer be volatile? explain why.
3); What is wrong with the following function:
int square (volatile int *ptr)
{
return *ptr * *PTR;
}
Here's the answer:
1) Yes. An example is a read-only status register. It is volatile because it can be changed unexpectedly. It is const because the program should not attempt to modify it.
2); Yes. Although this is not very common. An example is when a service subroutine fixes a pointer that points to a buffer.
3) This piece of code is a bit perverted. The purpose of this code is to return the pointer *ptr to the square of the value, but since *ptr points to a volatile parameter, the compiler will produce code similar to the following:
int square (volatile int *ptr)
{
int A, B;
A = *ptr;
b = *ptr;
return a * b;
}
Because the values of the *ptr can be unexpectedly changed, A and B may be different. As a result, this code may return to the square value you expect! The correct code is as follows:
Long Square (volatile int *ptr)
{
int A;
A = *ptr;
Return a * A;
}
Bit operation (bit manipulation)
9. The embedded system always wants the user to perform bit operation on the variable or register. Given an integer variable A, write two segments of code, the first one sets a bit 3, and the second clears the bit 3 of a. In the above two operations, keep the other bits intact.
There are three basic reactions to this problem.
1) do not know how to start. The quilt cover has never done any embedded system work.
2) Use bit fields. Bit fields are something that is thrown into the C-language corner, which guarantees that your code is not portable between different compilers, and that your code is not reusable. I recently unfortunately saw Infineon write a driver for its more complex communication chip, which used bit fields so it was totally useless to me because my compiler implemented bit fields in other ways. From the moral: never let a non-embedded guy stick to the edge of the actual hardware.
3) operate with #defines and bit masks. This is a highly portable approach that should be used. The best solution is as follows:
#define BIT3 (0x1 << 3)
static int A;
void Set_bit3 (void)
{
a |= BIT3;
}
void Clear_bit3 (void)
{
a &= ~bit3;
}
Some people like to define a mask for setting and clearing values and defining some description constants, which is acceptable. I want to see a few points: Description constants, |=, and &=~ operations.
Access to fixed memory locations (accessing fixed locations)
10. Embedded systems often have features that require programmers to access a particular memory location. In a project, a value of 0XAA66 is required to set an integer variable with an absolute address of 0x67a9. The compiler is a purely ANSI compiler. Write the code to complete the task.
This question tests whether you know that in order to access an absolute address it is legal to cast an integral number (typecast) as a pointer. The way this problem is implemented varies with individual style. A typical similar code looks like this:
int *ptr;
PTR = (int *) 0x67a9;
*ptr = 0xaa55;
A more obscure approach is:
One of the more obscure methods is:
* (int * const) (0X67A9) = 0xaa55;
Even if your tastes are closer to the second option, I suggest you use the first option in your interview.
Interrupt (interrupts)
11. Interrupts are an important part of the embedded system, which has led to a number of compiler developers providing an extension-allowing standard C to support interrupts. The fact that it is represented is that a new keyword __interrupt has been produced. The following code uses the __interrupt keyword to define an interrupt service subroutine (ISR), please comment on this code.
__interrupt Double Compute_area (double radius)
{
Double area = PI * radius * RADIUS;
printf ("\narea =%f", area);
return area;
}
There are so many mistakes in this function that you don't know where to start:
1) The ISR cannot return a value. If you do not understand this, then you will not be employed.
2) The ISR cannot pass parameters. If you do not see this, your chances of being employed are equal to the first item.
3) In many processors/compilers, floating-point is generally non-reentrant. Some processors/compilers require the register to be placed in the stack, and some processors/compilers do not allow floating-point operations in the ISR. In addition, the ISR should be short and efficient, and it is unwise to do floating-point operations in the ISR.
4) with the 3rd same strain, printf () often has re-entry and performance problems. If you lose the third and 4th, I will not be too difficult for you. Needless to say, if you can get the post two o'clock, then your employment prospects are getting brighter.
Example code examples
12. What is the output of the following code and why?
void foo (void)
{
unsigned int a = 6;
int b =-20;
(A+b > 6)? Puts ("> 6"): Puts ("<= 6");
}
This question tests whether you understand the principle of automatic conversion of integers in C, and I find that some developers know very little about these things. However, the answer to this unsigned integer question is that the output is ">6". The reason is that when there are signed and unsigned types in an expression, all operands are automatically converted to unsigned types. So 20 becomes a very large positive integer, so the expression evaluates to more than 6. This is important for embedded systems that should be used frequently for unsigned data types. If you answer the wrong question, you will not get to the edge of the job.
13. Evaluate the following code snippet:
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1 ' s complement of zero */
For an int type that is not a 16-bit processor, the above code is not correct. Should be prepared as follows:
unsigned int compzero = ~0;
This problem really reveals whether the test taker understands the importance of processor word length. In my experience, a good embedded programmer is very precise about the details of the hardware and its limitations, but the PC program often takes the hardware as an unavoidable annoyance.
At this stage, candidates are either completely depressed or confident. If obviously the candidate is not very good, then the test is over here. But if the candidate does a good job, then I throw out the following additional questions, which are difficult, and I think only very good candidates can do well. To raise these questions, I would like to see more of the candidates coping with the problem rather than the answer. No matter what, you should be the entertainment bar ...
Dynamic memory allocation (allocation)
14. Although not as common as non-embedded computers, embedded systems have a process of dynamically allocating memory from the heap. So what are the possible problems with the dynamic allocation of memory in an embedded system?
Here, I expect the candidate to refer to memory fragmentation, fragmentation collection issues, variable time-of-hold, and so on. This topic has been extensively discussed in the ESP magazine (mainly P.J Plauger, whose explanation is far more than any explanation I can mention here), and all go back and look at the magazines! After getting the test taker into a false sense of security, I came up with such a little show:
What is the output of the following code fragment and why?
Char *ptr;
if (ptr = (char *) malloc (0)) = = NULL)
Puts ("Got a null pointer");
Else
Puts ("Got a valid pointer");
This is an interesting question. Recently, a colleague of mine accidentally passed the 0 value to the function malloc and got a valid pointer before I thought about the problem. This is the code above, the output of this code is "Got a valid pointer". I use this to start discussing such a problem and see if the interviewee thinks the library routines are doing the right thing. It's important to get the right answer, but the solution to the problem is more important than the rationale for your decision.
Typedef
In C, the Typedef is frequently used to declare a synonym for a data type that already exists. You can also use a preprocessor to do something similar. For example, consider the following example:
#define DPS struct S *
typedef struct S * tPS;
The intent of both of these cases is to define DPS and TPS as a pointer to the struct S. Which method is better? (if any) why?
This is a very delicate question, and anyone who answers the question correctly (for the right reason) should be congratulated. The answer is: TypeDef is better. Consider the following example:
DPS P1,P2;
TPS P3,P4;
The first extension is a
struct S * p1, p2;
.
The code above defines P1 as a pointer to a struct, p2 as an actual structure, which may not be what you want. The second example correctly defines the P3 and P4 two pointers.
The Obscure grammar
16. C language agree to some shocking structure, the following structure is legal, if it is what it does?
int a = 5, B = 7, C;
c = a+++b;
This question will be done as a happy ending to this quiz. Whether you believe it or not, the above example is entirely grammatical. The question is how does the compiler handle it? A low-level compilation author actually argues that the compiler should be able to handle as many legitimate usages as possible, according to the most processing principle. Therefore, the above code is processed into:
c = a++ + b;
Therefore, this code is held after a = 6, B = 7, c = 12.
If you know the answer, or guess the right answer, do it well. If you don't know the answer, I don't think of it as a problem. I find the biggest benefit of this problem is that it is a good topic about code writing style, code readability, code modification.
Embedded C language Face test