Introduction
I’ve seen a lot of lists of so-called interview questions for C that are pretty bad, e.g., “What are all of C’s built-in types?” Such questions are bad because they require only rote memorization and not any actual understanding of anything.
When I’ve interviewed candidates, I split my questions into (at least) two types:
- Programming language knowledge (to ensure the candidate actually knows at least the bare minimum of a specific language).
- Algorithm implementation (that I’ve allowed candidates to implement in any language of their choosing, or even pseudocode, since the goal is to determine their problem-solving ability).
For #1, I further split questions into two levels: “any” or “senior” level programmer. I ask whichever set of questions based on what the job requires.
Here are a few questions (with answers) I’d ask a candidate during an interview for a job programming in C — or C++ since all these questions are also valid C++ questions (but see the note at the end).
If you’re a beginner, I recommend trying to answer the questions for yourself before clicking on the Answer links.
Questions
Question 1: Arrays vs. Pointers
Given this code fragment:
const char a[] = "hello";
const char *p = "world";
What are the differences between a
and p
?
Answer
a
is an array of char
of 6 elements. It uses 6 bytes of memory in total. The name a
is constant, e.g., ++a
would be illegal.p
is a pointer variable pointing to an array of char
of 6 elements. It uses 6 bytes for the array + 8 bytes (on a 64-bit system) for the pointer or 14 bytes of memory in total. The name p
is variable, e.g., ++p
would increment it by 1 byte.
Question 2: C Strings
Given this code fragment:
char *s = "hello, world";
s[0] = 'H';
What does this code do?
Answer
There’s no definitive answer because it would result in undefined behavior, though it would probably crash. String literals are typically stored in a read-only memory segment, so attempting to overwrite one would be “bad.”
Question 3: struct
Memory Layout
Given this code:
struct S {
int i;
char c;
};
int main() {
struct S s1;
printf( "%zu\n", sizeof( s1 ) );
}
Question 3a: If compiled (with no compiler options) and run, what is a reasonable value for this program to print and why?
Some candidates try to include compiler options or attributes for packed structures at which point I have to emphasize that the code is to be compiled as-is with no compiler options or attributes. If the candidate does give an odd number, I then ask them to consider an array of the and draw the bytes in memory starting at Answer
8 — because of padding. 16 is also an acceptable answer. Any odd number, e.g., 5, is not acceptable.
struct
like:
struct S a[2]; // assume array starts at memory address 0x1000
0x1000
and then ask about what address a[1].i
is at. Hopefully at this point, the candidate will realize why an odd number is a bad answer and give a better answer.
Question 3b: Why is padding necessary?
Answer
Because many CPU architectures require that reads and writes for certain data types, e.g., integers and pointers, take place on a word boundary.
Question 4: Local Variables
Given this function (where T
is some arbitrary type that doesn’t matter here):
T* f() {
T t;
// ...
return &t;
}
Question: What’s wrong with this function?
Answer
Because t
is a local variable, it will cease to exist upon return, hence the pointer will be a dangling pointer. Attempting to dereference the pointer would result in undefined behavior (and would likely result in a core dump, if you’re lucky).
Question 5: static
Given:
static int g;
int f() {
static int i;
return ++i;
}
Question 5a: What does the static
for the declaration of g
do?
Answer
It makes g
have internal linkage, that is the name of g
can only be used from the file it’s in. It’s like “private” for the file. The program could have another g
in another file and they would be distinct variables.
Question 5b: What does the static
for the declaration of i
do?
Answer
It makes i
be initialized to 0 and continue to exist between calls to f()
.
Question 5c: What value does this function return the second time it’s called?
Answer
Two. It’s 0 initially, then the first ++i
makes it 1 and the second ++i
makes it 2.
Question 5d (senior): Can this function ever result in undefined behavior?
Answer
Yes, because of signed integer overflow.
Question 5e (senior): How can you eliminate the possibility of undefined behavior?
Answer
Make i
be unsigned
since unsigned integer overflow is well-defined (it wraps around to 0).
Question 5f (senior): Is f()
thread-safe? Why or why not?
Answer
No, because the increment of i
is not thread-safe because i
is neither _Atomic
nor are mutexes nor locks being used.
Question 6 (Senior): free()
How is free()
typically implemented by the C standard library given that you don’t pass it the amount of memory to be freed? How does it know how much memory to free?
To make this a C++ question, change
free
todelete
. Their implementations are similar or in some casesdelete
callsfree
under the hood.
When Answer
One way to implement free()
is to implement malloc(n)
to:
n
+ sizeof(size_t)
bytes.n
at the start of the allocated memory area.sizeof(size_t)
.free(p)
is called:
((size_t*)p)[-1]
.sizeof(size_t)
.
Conclusion
Those are some decent C interview questions. Feel free to use them when interviewing candidates.
Note: As mentioned, I’d ask these same questions of a candidate interviewing for a job programming in C++. However, I’d also ask additional questions, but that’s a story for another time.