C Interview Questions

Paul J. Lucas - Aug 15 - - Dev Community

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:

  1. Programming language knowledge (to ensure the candidate actually knows at least the bare minimum of a specific language).
  2. 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";
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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 ) );
}
Enter fullscreen mode Exit fullscreen mode

Question 3a: If compiled (with no compiler options) and run, what is a reasonable value for this program to print and why?

Answer
8 — because of padding. 16 is also an acceptable answer. Any odd number, e.g., 5, is not acceptable.

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 struct like:

struct S a[2];  // assume array starts at memory address 0x1000
Enter fullscreen mode Exit fullscreen mode

and draw the bytes in memory starting at 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;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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 to delete. Their implementations are similar or in some cases delete calls free under the hood.

Answer
One way to implement free() is to implement malloc(n) to:
  1. Allocate n + sizeof(size_t) bytes.
  2. Store n at the start of the allocated memory area.
  3. Return that address + sizeof(size_t).

When free(p) is called:

  1. Get the number of bytes from ((size_t*)p)[-1].
  2. Deallocate that number of bytes + 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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .