9 Essential C Interview Questions *

Toptal sourced essential questions that the best C developers and engineers can answer. Driven from our community, we encourage experts to submit questions and offer feedback.

Hire a Top C Developer Now
Toptal logois an exclusive network of the top freelance software developers, designers, finance experts, product managers, and project managers in the world. Top companies hire Toptal freelancers for their most important projects.

Interview Questions


What is a void pointer? Can you dereference a void pointer without knowing its type?

View answer

A void pointer is a pointer that can be used to point to any data of any arbitrary type. A void pointer can be dereferenced only after explicit casting. For example:

int a = 5;
void *b = &a;
printf(“%d\n”, *((int*)b));

What is the difference between #include "..." and #include <...>?

View answer

The difference lies in where the preprocessor looks for the file to be included. For the include directive with a double quoted filename, the preprocessor limits its search for the file to the same directory where the current source file resides, and then to the standard directories pre-designated by the compiler. On the other hand, when the directive uses angle brackets, the preprocessor searches for the file in directories pre-designated by the compiler - usually directories where standard library header files reside.


What are dangling pointers? How are dangling pointers different from memory leaks?

View answer

Dangling pointers are those that point to memory locations which have already been freed. For example:

int *a = malloc(sizeof(int));
// a is now a dangling pointer

Memory leaks are quite the opposite of dangling pointers. Memory leaks happen when memory locations are not freed, but there is no way to refer to them (i.e., no pointers are pointing to them).

int *a = malloc(sizeof(int));
a = 0;
// now a no longer points to the memory that we just allocated, causing a memory leak

Unlike higher-level languages with garbage collectors, it is critical to always keep track of allocated memory when programming in C.

Apply to Join Toptal's Development Network

and enjoy reliable, steady, remote Freelance C Developer Jobs

Apply as a Freelancer

What is being declared in the following statement?

char (*x) (char*);
View answer

The statement above declares x as a pointer to a function that takes a single character-pointer argument, and returns a character.


What will be the output when the following code is executed? Explain.

#include <stdio.h>
#define SQUARE(a) (a)*(a)

int main() {
    printf("%d\n", SQUARE(4));
    int x = 3;
    printf("%d\n", SQUARE(++x));
View answer

The answer is infact undefined, and depends on the compiler being used. Some compilers will result in 16 and 20, while others will produce 16 and 25.

One might expect the second use of the SQUARE macro to yield 16, just like the first use of the SQUARE macro. However, macros are processed by the preprocessor, a step that takes place before actual compilation begins. Expanding the second macro will show what actually gets compiled:


The evaluation of the pre-increment operation ++x is where the undefined behavior in C comes in. With some compilers, the macro will reduce to (4)*(5), while in other cases, it will be evaluated as (5)*(5).

This article discusses this behavior further.


Why is it usually a bad idea to use gets()? Suggest a workaround.

View answer

The function gets() reads characters from the stdin and stores them at the provided input buffer. However, gets() will keep reading until it encounters a newline character. Unless the buffer is large enough, or the length of the line being read is known ahead of time, gets() can potentially overflow the input buffer and start overwriting memory it is not supposed to, wreaking havoc or opening security vulnerabilities.

One way to work around this issue is to use fgets(). It allows you to put a limit on the maximum number of characters to read:

fgets(b, 124, stdin);

What is the difference between structs and unions?

View answer

A struct is a complex data type that allows multiple variables to be stored in a group at a named block of memory. Each member variable of a struct can store different data, and they all can be used at once.

struct a {
    int x;
    char y;
} a;

For example, you may store an integer in x, and and a character in y above, independent of each other.

A union, on the other hand, stores the contents of any member variable at the exact same memory location. This allows the storage of different types of data at the same memory location. The result is that assigning a value to one member will change the value of all the other members. Unlike struct, only one member of the union type is likely to be useful at any given time.

union b {
    int x;
    char y;
} b;

For example, storing a character in y may automatically change the integer you read from x to something meaningless or unpredictable.


This code snippet converts a floating point number to an integer using casting:

float f = 1.0;

int i1 = (int) f;
int i2 = * (int *) &f;

printf("%d\n, i1);
printf("%d\n, i2);

The following output is produced:


Can you explain why results differ?

View answer

The first casting operation properly converts from a floating point number to an integer, as specified by the C standard. The second conversion, however, is first casting a float pointer to an integer pointer which is then dereferenced to get the final result. This way the compiler is effectively treating raw bits from a float (typically stored in IEEE floating point format) as if they were bits of an integer. Besides getting a wrong result you are potentially doing a “bad read” operation, in cases where sizeof(int) is greater than sizeof(float) (e.g. on some 64-bit architectures).

Although this particular code is unlikely, it demonstrates one of the risks involved in typecasting when only a pointer to the variable to be cast is available. In practice, the pointer must be dereferenced before it is cast.


What is the output of the following program if run on a 32-bit operating system?

#include <stdio.h>
int  main()
  int a=-2;
View answer

In a 32 bit operating system, integers are stored as 4 bytes.

Since a is negative, it will be stored in 2’s complement. When an integer is negative and we want to right shift by “n” bits, we need to prepend ones (not zeros!) to the left hand side. The answer would therefore be 0xFFFF (%x prints out value in hex).

There is more to interviewing than tricky technical questions, so these are intended merely as a guide. Not every “A” candidate worth hiring will be able to answer them all, nor does answering them all guarantee an “A” candidate. At the end of the day, hiring remains an art, a science — and a lot of work.

Why Toptal

Tired of interviewing candidates? Not sure what to ask to get you a top hire?

Let Toptal find the best people for you.

Hire a Top C Developer Now

Our Exclusive Network of C Developers

Looking to land a job as a C Developer?

Let Toptal find the right job for you.

Apply as a C Developer

Job Opportunities From Our Network

Submit an interview question

Submitted questions and answers are subject to review and editing, and may or may not be selected for posting, at the sole discretion of Toptal, LLC.

* All fields are required

Looking for C Developers?

Looking for C Developers? Check out Toptal’s C developers.

James Cole

Freelance C Developer
Toptal Member Since June 18, 2020

James has been an independent consultant for 25 years employing a wide range of technologies and languages. He is passionate about software, consistently upgrading his skills and staying on top of new technologies while leading teams, conducting JADS, and giving presentations.

Show More

Phoebe Beckinger

Freelance C Developer
United States
Toptal Member Since April 15, 2020

Phoebe is a full-stack developer with over nine years of experience building software across a range of industries and company sizes. Her primary focus has been server-side development, but she also enjoys working with modern UI solutions. Phoebe is meticulous about her code, a passionate advocate for best practices, and knows how fundamental communication is in delivering exceptional products.

Show More

Nikola Vasović

Freelance C Developer
Toptal Member Since October 4, 2019

As a software engineering master's graduate and the holder of a prestigious national scholarship, Nikola kickstarted his full-stack developer career at Toptal. Leveraging expertise in Flutter for cross-platform mobile development, he specialized in streamlining MVP product launches for startups. Nikola co-founded and acted as the CTO of Varius Development and extended his DevOps and PM expertise while collaborating with prominent global enterprises, transforming into a versatile IT professional.

Show More

Toptal Connects the Top 3% of Freelance Talent All Over The World.

Join the Toptal community.

Learn more