C language testing is an essential and effective way to recruit embedded system programmers. Over the years, I have both participated in and organized many such tests. During this process, I realized that these tests can provide a lot of useful information for both the interviewer and the interviewee. In addition, aside from the interview pressure, this test is also quite interesting.
From the perspective of the subject, you can learn a lot about the problem or invigilator. Is this test designed to show the ANSI standard details rather than technical skills? Is this stupid question? To obtain the ASCII value of a character. Do these questions focus on your system call and memory allocation policy capabilities? This indicates that the author may spend time on a microcomputer rather than on an embedded system. If the answer to any of the above questions is "yes", I know that I have to seriously consider whether I should do the job.
From the perspective of the interviewer, a test may reveal the examinee's quality in many aspects: the most basic, you can understand the examinee's C language level. No matter how it works, it's interesting to see how this person answers questions he doesn't know. Should the examinee make a wise choice with a good intuition, or is it just blind? When the examinee gets stuck on a problem, is it an excuse? Is it a real curiosity about the problem? Does it look like a learning opportunity? I found this information as useful as their test scores.
With these ideas, I decided to give some questions that really target embedded systems. I hope these headaches can help people who are looking for a job. I have encountered these problems over the years. Some of these questions are difficult, but they should give you some inspiration. This test is suitable for candidates of different levels. Most primary-level candidates have poor performance, and experienced programmers should have good performance. In order for you to determine the preference of some questions, no score is assigned for each question. If you choose these questions for your use, please assign scores according to your meaning.
Preprocessor)
1. Use the preprocessing command # define to declare a constant to indicate the number of seconds in a year (ignore the leap year problem)
# Define seconds_per_year (60*60*24*365) UL
Here I want to see several things:
?; # Basic knowledge of define syntax (for example, it cannot end with a semicolon or use of parentheses)
?; Knowing that the pre-processor will calculate the value of a constant expression for you, therefore, it is clearer and easy to directly write how much seconds you calculate in a year rather than calculate the actual value.
?; Realize that this expression will overflow the integer number of a 16-bit machine-so the long integer sign L is used to tell the compiler that this constant is the long integer number.
?; If you use ul (representing an unsigned long integer) in your expression, you have a good starting point. Remember, the first impression is very important.
2. Write a "standard" macro min, which inputs two parameters and returns a smaller one.
# Define min (A, B) (a) <= (B )? (A): (B ))
This test is intended for the following purposes:
?; ID # basic knowledge of define application in macros. This is important, because until the inline operator becomes part of standard C, macros are the only way to facilitate the generation of embedded code. For embedded systems, in order to achieve the required performance, embedded code is often a required method.
?; Knowledge of triple-condition operators. The reason for the existence of this operator in C language is that it enables the compiler to produce code that is more optimized than if-then-else. It is important to understand this usage.
?; Be careful to enclose parameters in brackets in macros
?; I also use this question to discuss the side effects of macros. For example, what happens when you write the code below?
Least = min (* P ++, B );
3. pre-processor ID # What is the purpose of error?
If you do not know the answer, see References 1. This problem is useful for distinguishing a normal guy from a nerd. Only the nerd can read the appendix of the C language textbook to find the answer to such a question. Of course, if you are not looking for a nerd, you 'd better ask yourself not to know the answer.
Infinite Loops)
4. Infinite loops are often used in embedded systems. How do you write an infinite loop in C?
There are several solutions to this problem. My preferred solution is:
While (1)
{
?}
Some programmers prefer the following solutions:
For (;;)
{
?}
This implementation method makes me embarrassed because this syntax does not exactly express what is going on. If a candidate provides this solution, I will use this as an opportunity to explore the basic principles of their practice. If their basic answer is: "I was taught to do this, but I never thought of why ." This leaves a bad impression on me.
The third solution is to use GOTO
Loop:
...
Goto loop;
If the candidate gives the above solution, it indicates that he is an assembly language programmer (which may be a good thing) or a basic/Fortran programmer who wants to enter a new field.
Data declarations)
5. Use variable A to give the following definition
A) An integer)
B) a pointer to an integer (a pointer to an integer)
C) a pointer to a pointer pointing to an integer (a pointer to an intege) r
D) an array of 10 integers (an array of 10 integers)
E) an array with 10 pointers pointing to an integer. (An array of 10 pointers to integers)
F) A pointer to an array with 10 integers (a pointer to an array of 10 integers)
G) a pointer to a function. The function has an integer parameter and returns an integer number (a pointer to a function that takes an integer as an argument and returns an integer)
H) An array with 10 pointers pointing to a function, this function has an integer parameter and returns an integer (an array of ten pointers to functions that take an integer argument and return an integer)
The answer is:
A) int A; // an integer
B) int * A; // a pointer to an integer
C) int ** A; // 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
People often claim that there are several questions that can be answered only after a book is opened. I agree with this statement. When I wrote this article, I checked the book to confirm the correctness of the syntax. But when I was interviewed, I expected to be asked this question (or similar questions ). Because I knew the answer to this question during the interview period. If the examinee does not know all the answers (or at least most of the answers), he will not be prepared for the interview. If the interviewer is not prepared for the interview, why should he prepare?
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 obvious functions:
?; In the function body, a variable declared as static remains unchanged when the function is called.
?; In a module (but in the external body of a function), a variable declared as static can be accessed by the function used in the module, but cannot be accessed by other functions outside the module. It is a local global variable.
?; In a module, a function declared as static can only be called by other functions in this module. That is, this function is restricted to use within the local scope of the module that declares it.
Most of the respondents can answer part 1 correctly. Some of them can answer part 2 correctly. Few people can understand part 3. This is a serious disadvantage of a candidate because he obviously does not understand the benefits and importance of localized data and code scope.
Const
7. What does the const keyword mean?
As soon as I hear the subject say, "const means constant", I know that I am dealing with an amateur. Last year, Dan Saks has completely summarized all const usage in his article, So ESP (TRANSLATOR: embedded systems programming) every reader should be very familiar with what const can and cannot do. if you have never read that article, you only need to say that const means "read-only. Although this is not a complete answer, I accept it as a correct answer. (If you want to know more detailed answers, read the Saks Article carefully .)
If the examinee can answer this question correctly, I will ask him an additional question:
What does the following statement mean?
Const int;
Int const;
Const int *;
Int * const;
Int const * a const;
/*****/The first two functions are the same. A is a constant integer. Third, it means that a is a pointer to a constant INTEGER (that is, an integer cannot be modified, but a pointer can be ). Fourth, a is a constant pointer to an integer (that is, the integer to which the Pointer Points can be modified, but the pointer cannot be modified ). The last one means
Is a constant pointer to a constant INTEGER (that is, the pointer to an integer cannot be modified, and the pointer cannot be modified ). If the examinee can answer these questions correctly, he will leave a good impression on me. By the way, you may ask, even if you don't need the keyword const, it is still easy to write a program with the correct function. Why should I pay so much attention to the keyword const? I also have the following reasons:
?; The key word const is used to send very useful information to the reader of your code. In fact, it declares a parameter as a constant to tell the user the purpose of applying this parameter. If you spend a lot of time cleaning up the garbage left by others, you will soon learn to thank this excess information. (Of course, few programmers know how to use Const.
The garbage left by others .)
?; By attaching some information to the optimizer, using the keyword const may produce more compact code.
?; The rational use of the keyword const can enable the compiler to naturally protect those parameters that do not want to be changed, so as to prevent them from being accidentally modified by code. In short, this can reduce the occurrence of bugs.
Volatile
8. What does the keyword volatile mean? Three different examples are provided.
A variable defined as volatile means that this variable may be unexpectedly changed, so that the compiler will not assume the value of this variable. Precisely, the optimizer must carefully re-read the value of this variable every time when using this variable, rather than using the backup stored in the register. The following are examples of volatile variables:
?; Hardware registers of parallel devices (for example, Status Registers)
?; Non-automatic variables accessed in an interrupt service subroutine)
?; Variables shared by several tasks in multi-threaded applications
People who cannot answer this question will not be hired. I think this is the most basic problem to distinguish between C programmers and embedded system programmers. Embedded people often deal with hardware, interruptions, RTOS, etc. All of these require volatile variables. If you do not know volatile content, it will lead to disasters.
If the subject correctly answers this question (well, I suspect it will be the case), I will go a little deeper to see if this guy understands the full importance of volatile.
?; Can a parameter be const or volatile? Explain why.
?; Can a pointer be volatile? Explain why.
?; What are the following function errors:
Int square (volatile int * PTR)
{
Return * PTR ** PTR;
}
The answer is as follows:
?; Yes. One example is read-only status registers. It is volatile because it may be unexpectedly changed. It is const because the program should not try to modify it.
?; Yes. Although this is not very common. One example is when a service subroutine repairs a pointer to a buffer.
?; This code is a little abnormal. The purpose of this Code is to return the pointer * PTR points to the square of the value. However, since * PTR points to a volatile parameter, the compiler will generate code similar to the following:
Int square (volatile int * PTR)
{
Int A, B;
A = * PTR;
B = * PTR;
Return a * B;
}
* The value of * PTR may be unexpectedly changed, so a and B may be different. As a result, this Code may not return the expected square value! The correct code is as follows:
Long square (volatile int * PTR)
{
Int;
A = * PTR;
Return a *;
}
Bit manipulation)
9. In an embedded system, you must perform bit operations on variables or registers. Given an integer variable A, write two pieces of code. The first one sets bit 3 of A, and the second one clears bit 3 of. In the preceding two operations, you must keep the other bits unchanged.
There are three basic responses to this problem:
?; I don't know how to start. The quilt has never worked on any embedded system.
?; Use bit fields. Bit fields is thrown into the dead corner of the C language, which ensures that your code is different
Compilers cannot be transplanted, and your Code cannot be reused. Recently, unfortunately, I have seen infineon write drivers for its more complex communication chip. It uses bit fields and is completely useless to me, because my compiler uses other methods to implement bit fields. Morality: Never let a non-embedded guy stick the actual hardware edge.
?; Use # defines and bit masks. This is a highly portable method and should be used.
The best solution is as follows:
# Define bit3 (0x1 <3)
Static int;
Void set_bit3 (void ){
A | = bit3;
}
Void clear_bit3 (void ){
A & = ~ Bit3;
}
Some people like to define a mask to set and clear values and define some descriptive constants at the same time, which is acceptable.
I want to see several key points: constant, | = and & = ~ Operation.
Accessing fixed memory locations)
10. embedded systems often require programmers to access a specific memory location. In a project, the value of an integer variable whose absolute address is 0x67a9 must be set to 0xaa66. The compiler is a pure ANSI compiler. Write code to complete this task.
Test whether you know that it is legal to forcibly convert an integer number (typecast) into a pointer to access an absolute address. The implementation of this problem varies with the individual style. The typical code is as follows:
Int * PTR;
PTR = (int *) 0x67a9;
* PTR = 0xaa55;
A more obscure approach is:
A relatively obscure method is:
* (Int * const) (0x67a9) = 0xaa55;
Even if your taste is closer to the second option, I suggest you use the first option during the interview.
Interrupts)
11. interruption is an important part of embedded systems, which causes many compilation developers to provide an extension to interrupt Standard C support. It indicates that a new keyword _ interrupt is generated. 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;
Printf ("/narea = % F", area );
Return area;
}
There are too many errors in this function, so people don't know where to start:
?; ISR cannot return a value. If you do not understand this, you will not be hired.
?; ISR cannot pass parameters. If you do not see this, your chances of being hired are equivalent to the first.
?; In many processors/compilers, floating points are generally not reentrant. Some processors/compilers need to import the registers at the amount into the stack. Some processors/compilers do not allow floating-point operations in ISR. In addition, ISR should be short and efficient, and it is unwise to perform floating point operations in ISR.
?; With the 3.1 pulse, printf () often has re-import and performance problems. If you lose the third and fourth points, I won't be too embarrassed. Needless to say, if you can get the last two points, your prospects for employment will become brighter.
*****
Code example)
12. What is the output of the following code? Why?
Void Foo (void)
{
Unsigned int A = 6;
Int B =-20;
(A + B> 6 )? Puts ("> 6"): puts ("<= 6 ");
}
This problem tests whether you understand the principle of automatic Integer Conversion in the C language. I found that some developers know very little about these things. In any case, the answer to this unsigned integer is "> 6 ". The reason is that when there are signed and unsigned types in the expression, all operands are automatically converted to the unsigned type. Therefore,-20 is a very large positive integer, so the result calculated by this expression is greater than 6. This is very important for embedded systems that should frequently use the unsigned data type. If you fail to answer this question, you will not be able to get the job.
13. Evaluate the following code snippet:
Unsigned int zero = 0;
Unsigned int compzero = 0 xFFFF;
/* 1's complement of zero */
For a 16-bit processor of the int type, the above Code is incorrect. It should be written as follows:
Unsigned int compzero = ~ 0;
This question reveals whether the examinee understands the importance of the processor font length. In my experience, good embedded programmers can understand hardware details and its limitations very accurately. However, PC programs often treat hardware as an unavoidable headache. At this stage, the examinee is either totally frustrated or confident. If it is clear that the candidate is not good, the test will end here. However, if the candidates are doing well, I will throw the following append questions. These questions are difficult. I think only very good candidates can do well. To raise these questions, I hope to see more methods for the examinee to cope with the questions, rather than the answers. In any case, you should be entertaining.
...
Dynamic Memory Allocation (dynamic memory allocation)
14. Although not as common as embedded computers, embedded systems still have the process of dynamically allocating memory from heap. In the embedded system, what are the possible problems with dynamic memory allocation? Here, I expect the examinee to mention memory fragmentation, fragment collection issues, variable holding time, and so on. This topic has been widely discussed in the ESP magazine (mainly P. j. plauger, his explanation is far more than anything I can mention here). Let's look back at these magazines! After putting the examinee into a false sense of security, I came up with a small program:
What is the output of the following code snippet? Why?
Char * PTR;
If (PTR = (char *) malloc (0) =
Null)
Else
Puts ("got a null pointer ");
Puts ("got a valid Pointer ");
This is an interesting question. Recently, one of my colleagues inadvertently passed the 0 value to the malloc function. After obtaining a valid pointer, I came up with this question. This is the above Code. The output of this code is "got a valid Pointer ". I used this to discuss such a problem and see if the subject thought of the library routine. It is important to get the correct answer, but the solution to the problem and the basic principles of your decision are more important.
Typedef
15 typedef is frequently used in C to declare an existing data type. You can also use a pre-processor to do similar things. For example, consider the following example:
# Define DPS struct s *
Typedef struct s * TPS;
The intention of the above two cases is to define the DPS and TPS as a pointing structure s pointer. Which method is better? (If so) Why?
This is a very subtle question. Anyone should be congratulated on answering this question (the legitimate reason. The answer is: typedef is better.
Consider the following example:
DPS P1, P2;
TPS P3, P4;
The first extension is
Struct s * P1, P2;
.
The code above defines P1 as a point to the structure, and P2 as an actual structure, which may not be what you want. The second example correctly defines the P3 and P4 pointers.
Obscure syntax
16. The C language agrees to some shocking structures. Is the following structure legal? If so, what does it do?
Int A = 5, B = 7, C;
C = A ++ B;
This question will end happily in this quiz. Whether you believe it or not, the above example is completely syntactic. The problem is how does the compiler handle it? The authors of low-level compilation will actually argue about this issue. According to the best processing principle, the compiler should be able to handle as many valid usage as possible. Therefore, the above Code is processed:
C = A ++ B;
Therefore, a = 6, B = 7, c = 12 after the code is executed.
If you know the answer or guess the correct answer, you can do well. If you do not know the answer, I will not regard it as a question. I found that the biggest advantage of this problem is that it is a good topic about the code writing style, code readability, and code modifyability.
Okay, guys. You have finished all the tests now. This is my C language test question. I wrote it with pleasure. I hope you can read it in the same mood. If you think this is a good test, try to use it in your job search process. The Sky knows that maybe after a year or two, I will not do my current job. I also need to find one.
Nikel Jones is a consultant and now lives in Maryland. When he is not underwater, you can find him in multiple scope embedded projects. He was very pleased to receive a letter from the reader whose email address was: NAJones@compuserve.com.
References
?; Jones, Nigel, "in praise of the # error directive," embedded systemsprogramming, September 1999, p. 114.
?; Jones, Nigel, "efficient C code for eight-bit MCUs," embedded systems programming, November 1998, p. 66.