Embedded C/C++ Interview Notes – A Collection from Major Companies

Embedded C/C++ Interview Notes - A Collection from Major Companies

Comprehensive Notes on Embedded Programming Interviews

Compilation of programming-related questions encountered during real job applications at major companies.

2

0

2

5

Embedded C/C++ Interview Notes - A Collection from Major Companies

Based on my personal experiences during embedded interviews at various major companies (including top-tier companies), I am sharing my genuine interview notes for free. I hope everyone pays more attention to programming details.

Background: I have also struggled to find comprehensive materials and have paid for incomplete resources. Therefore, I wrote this based on my personal notes, hoping it will help you and inspire focus on programming details.

Audience: This is aimed at algorithm integration software development, embedded driver development, embedded software development, and imaging software development, all of which face similar questions.

Introduction

Interviews for imaging software mainly revolve around programming and imaging aspects. This section focuses on summarizing the commonalities in embedded programming, with a dedicated section on camera programming to follow.

Programming questions can be categorized into C and C++ languages; from a systems perspective, they can be divided into basic programming and system programming; based on the nature of the questions, they can be classified into basic questions and tricky situational questions; and finally, algorithm questions.

Given the overall length, this article is divided into two parts. This part mainly summarizes the basics of C and C++; the next part will cover system programming and basic algorithm questions;

The complete version includes code examples, a boon for lazy readers. You can contact me via WeChat for the complete version with random rewards.

1. C Language Programming Questions

Embedded C/C++ Interview Notes - A Collection from Major Companies

Basic questions mainly revolve around arrays and function pointers. Here, I offer a starting point and recommend saving this.

Commonly asked programming fundamentals include the usage of various common keywords; details on structs and data types; operations on pointer expressions; various arrays and pointers are key focus areas; special functions like callbacks and I/O functions are also frequently examined.

1

Key Point 1: Keywords

volatile Keyword

Question: Explain the meaning of the volatile keyword and when to use it.

Answer:

The volatile keyword tells the compiler that the variable it modifies may be changed outside the control of the program. Therefore, the compiler should read its value directly from memory every time it accesses the variable, rather than using a cached value that may have been stored in a register. This ensures the visibility and consistency of the variable.

Typically used for: accessing hardware registers, variables in interrupt service routines, and variables shared across multiple threads or tasks.

Example:

// Accessing a hardware register in an embedded system#define STATUS_REGISTER *((volatile unsigned int*)0x40021018) void check_status() { if (STATUS_REGISTER & 0x01) { // Handle specific status } }

static Keyword

Question: Explain the function of the static keyword on functions and variables, and the characteristics of local and global usage.

Answer:

(1) When used before a function, static limits the scope of that function to the file in which it is defined, making it a static function that cannot be called from other files.

(2) When used before a variable, static means that the variable’s lifetime spans the entire duration of the program, but its scope is still limited to where it is defined (e.g., inside a function or globally).

(3) Modifying local variables

When static is used to modify a local variable within a function, it changes the variable’s storage duration, allowing it to last for the entire duration of the program, but the variable is only visible within the function that defines it. Additionally, this variable is initialized only once when the function is first executed; even if the function is called multiple times, the variable’s value will retain the value from the last function call.

(4) Modifying global variables and functions

When static is used to modify global variables or functions, it limits the visibility of these variables or functions, making them only visible and usable within the file (compilation unit) that defines them, i.e., they have internal linkage.

Example:

const Keyword

Question: Explain the role of the const keyword and compare it with #define in defining constants.

Answer: The const keyword is used to declare a variable as a constant, meaning its value cannot be modified after initialization.

In terms of differences, const has a data type, while #define does not; const provides type checking, while #define is simply text replacement without type checking;

From a storage perspective, constants defined with const are stored in memory (static storage area or stack), while macros defined with #define are replaced during preprocessing and do not occupy memory space (though they can affect program length).

Example:

#define MAX_SIZE 100 const int MaxSize = 100; void func() { int array[MAX_SIZE]; // Using constant defined by #define int constArray[MaxSize]; // Using const definition

typedef Keyword

Question: Explain the role of the typedef keyword and the differences between typedef and #define.

Answer:

The typedef keyword is used to define a new name (alias) for an existing data type. It does not create a new data type but provides a more convenient and descriptive name for an existing type.

typedef is limited to defining symbolic names for types, while #define can define aliases for both types and values, for example, you can define 999 as MAX.

typedef is processed by the compiler, while #define statements are handled by the preprocessor.

Example:

typedef unsigned int uint; typedef struct { int x; int y; } Point;

setjmp

Question: Have you used setjmp? How does it differ from goto?

Answer:

Defined in #include setjmp.h.

Non-local jumps implemented by setjmp and longjmp provide a mechanism similar to goto, but are not limited to the scope of a single function.

Example:

2

Key Point 2: Variable Types

Data Structures: Stack and Queue

Question:

Have you used stacks and queues? What is the difference?

Answer:

Common data structures in embedded systems include linked lists, stacks, queues, trees, etc. In embedded systems, it is crucial to balance algorithm efficiency with resource consumption to choose suitable algorithms and data structures.

Stack: Last In, First Out (LIFO) – push and pop, suitable for scenarios that require preserving data order.

Queue: First In, First Out (FIFO), suitable for scenarios that require processing data in order, such as creating a queue to insert new elements and delete to remove an element.

Linked List: Suitable for scenarios that require frequent insertion and deletion operations.

Tree: Includes binary trees, red-black trees, etc., suitable for scenarios that require fast searching, inserting, and deleting operations.

Variable Definition

Question:

Do you define variables in header files?

Answer:

In special cases, you can declare variables using the extern keyword in a header file, while defining those variables in a specific .c file, allowing for sharing variables across multiple .c files.

Variables are typically defined in .c files rather than header files for several reasons:

(1) Avoiding redefinition: Header files are often included in multiple .c files. If you define a variable in a header file, it will be redefined every time that header file is included.

(2) Controlling variable scope and lifetime: Variables defined in .c files have file scope, meaning they are only visible and usable within that .c file. This helps with encapsulation and modular programming.

(3) Memory management: In embedded systems, memory resources are often very limited. Defining variables in .c files makes it easier to control and manage their memory allocation. These variables wrongly perceived as global variables may lead to memory leaks.

Register Variables

Question:

Have you used register variables?

Answer:

Register: Used to define local variables stored in registers rather than in RAM.

The characteristic is that the variable is accessed faster, but you cannot directly take its address.

Constant Pointers and Pointer Constants

Question:

What is the difference between constant pointers and pointer constants?

Answer:

A pointer constant refers to a pointer that points to constant data. This means you cannot modify the value of the data it points to through this pointer, but you can change the pointer itself (i.e., it can point to different addresses).

const int *ptr;

A constant pointer is a pointer whose value is constant, meaning you cannot change the address it points to, but you can modify the data it points to (if the data itself is not constant).

int *const ptr;

Type Casting

Type casting refers to converting a variable of one data type to another data type.

Commonly used when handling hardware registers, bit operations, and different data format conversions.

Example:

Struct Byte Alignment

Question:

In a struct defined on a 32-bit system, with members char a; int b; short c; how many bytes does it occupy?

Answer:

Byte alignment, 8 bytes, with the following other arrangements.

Example:

3

Key Point 3: Expression Operations

Comma Expression

Question:

In the comma operation c=a,b; what is the result of c?

Answer:

The comma operator is used to execute two expressions in sequence and returns the value of the last expression.

In c=a,b;, the comma operator is not used correctly. This will cause c to be assigned the value of a, while the value of b is not used in this expression.

d=(a,b); Here, the comma operator is used correctly. Therefore, d will be assigned the value of b because the value of a is computed but discarded by the comma operator.

Variable Increment and Decrement

Question:

What are the results of c=n++ and c=–n?

Answer:

Increment (++) and decrement (–) operations on variables are typically used in counting, state machine updates, and toggling flags in hardware register access.

These types of questions are commonly tested, and some written exam questions indeed present various odd scenarios.

Example:

Pointer Arithmetic

Question:

How do you manipulate array elements using pointers?

For example, given an integer array int arr[] = {1, 2, 3, 4, 5}; and a pointer int *ptr = arr;, write the expression to obtain the value of the third element in the array and explain your answer.

Answer:

int thirdValue = *(ptr + 2);

Pointer arithmetic actually increases the pointer’s address value by one or more times the size of the type it points to. For example, if there is a pointer to int int *ptr, and the int type occupies 4 bytes, then ptr + 1 will increase ptr’s address by 4 bytes, pointing to the next int’s location.

Pointer Subtraction

Question:

Given two pointers pointing to the same integer array int *start = arr; and int *end = arr + 4; (where arr is as above), please calculate and print how many elements are between these two pointers.

Answer:

int count = end – start; printf(“%d\n”, count);

Pointer subtraction is based on the size of the type being pointed to, reducing the address value. If ptr2 and ptr1 are pointers to the same type of data, then ptr2 – ptr1 yields the number of elements between the two pointers, not the byte count.

Example:

Array Pointer Addition

Question:

Given a pointer to a character array (string) char *str = “hello”;, write the expression to obtain the address of the second character (i.e., ‘e’) in the string and explain your answer.

Answer:

char *secondCharPtr = str + 1;

Example:

Multi-dimensional Array Addition

Question:

How do you use pointers to access elements in a two-dimensional array?

Answer:

A two-dimensional array is essentially an array of arrays, meaning each element is itself an array.

For a two-dimensional array, the array name acts as a pointer to its first row (the first one-dimensional array).

That is, the array name can be viewed as a pointer to a pointer to an array containing 4 integers (i.e., int (*)[4] type).

To access a specific element, you can first convert the array name to a pointer to a row, then add the row index (multiplied by the number of elements per row to calculate the offset), and finally add the column index:

int *ptr = (int *)array; // Convert array to a pointer to int// The conversion here is actually unsafe as it loses information about the number of columns in the array. The correct approach is to use a pointer of type int (*)[4].// However, we should view the array as a pointer to rows:

int (*rowPtr)[4] = array;int value = rowPtr[1][2]; // Correct way, but still using array indexing// Alternatively, using pointer arithmetic:int value2 = *((rowPtr + 1) + 2); // Access the second row, third column; note that here +2 is the column offset, with each element being the size of int// A clearer way is to use the correct type:int value3 = *(*(rowPtr + 1) + 2); // Equivalent to value2 but breaks down the pointer dereferencing steps// The more common way is to access via the row pointer and column index directly without converting to int*:int value4 = (*(rowPtr + 1))[2]; // Access the second row, third column

Example:

4

Key Point 4: Arrays and Pointers

Pointer Arrays and Array Pointers

Question:

What is the difference between pointer arrays and array pointers, and when to use each?

Answer:

A pointer array is an array whose elements are pointers, meaning each array element stores an address, typically pointing to other variables or arrays.

int a = 1, b = 2, c = 3;int *ptrArray[3] = {&a, &b, &c};

An array pointer is a pointer that points to an array, meaning the pointer stores the address of an array and knows the size of the array through its type.

int arr[5] = {1, 2, 3, 4, 5};int (*arrayPtr)[5] = &arr;

Example:

Struct Pointers

Question:

How do you use struct pointers?

Answer:

Structs as function parameters:

void printBook(struct Books book);

Pointer to struct:

struct Books *struct_pointer;

Example:

struct Books{ char title[50]; char author[50]; char subject[100]; int book_id;} book = {“C Language”, “RUNOOB”, “Programming Language”, 123456};

Function Pointers

Question:

How do you define a function pointer? What problems do function pointers mainly solve?

Answer:

typedef int (*test) (float *, float*);

test tmp;

Thus, tmp is a type of function pointer.

Function pointers are used to dynamically invoke different functions to handle different tasks.

Example:

Dangling Pointers

Question:

What is a dangling pointer, and why does it occur?

Answer:

A dangling pointer is a pointer that points to an unknown memory address.

Reasons for occurrence: not explicitly initialized; out-of-bounds array access; already freed (e.g., via free()), but subsequently accessed; pointer arithmetic errors.

Consequences: unpredictable behavior, including program crashes, data corruption, or security vulnerabilities.

4

Key Point 5: Functions

Function Return Values

Question:

How to correctly return a pointer without causing exceptions?

Answer:

Returning a pointer to a local variable from a function may cause pointer issues;

Returning a pointer to dynamically allocated memory from a function tends to have fewer pointer issues.

Signal Handling Functions

Question:

Do you know about signal handling functions? When would they be used?

Answer:

Signal handling functions are used to handle various signals encountered by the system during runtime.

For example, user interrupts (like Ctrl+C), hardware exceptions (like division by zero errors), and timer timeouts are all signals. Signals are asynchronous notification mechanisms used to inform processes that an event has occurred.

Main functions: capturing and handling signals; processing specific operations; making the program more robust.

Example:

signal.h

Terminate execution abort(void): Abnormally terminates a running program, raising the SIGABRT signal, for which a signal handling function can be set in the program.

Assert (assert): If the expression is false or 0, it prints a diagnostic message to standard error and terminates the program.

Callback Functions

Question:

How do you typically define and use callback functions?

Answer:

Callback functions are used as function pointers passed as parameters to another function.

They are often used to handle specific events, such as interrupts, timer expirations, or peripheral data readiness, making the code more modular and reusable.

Example:

**How to store a callback function in a struct and call it on a struct instance?**

File I/O Functions

Question:

How to read and parse characters within a file?

Answer:

Use I/O functions fopen and fget to obtain a file handle to read the character content within the file.

File opening and closing functions fopen and fclose;

File read/write functions fputc/fputs/fgetc/fgets, block read/write functions fread/fwrite, file pointer operations fseek/ftell.

Example:

Variable Arguments

Question:

Have you used variable arguments? How to implement summation using variable arguments?

Answer:

Variable arguments refer to functions that can accept a variable number of parameters.

This feature is implemented using header files in the standard library, which provides a set of macros to access these variable arguments.

Define function prototypes: use ellipses (…) to indicate that the function accepts a variable number of parameters.

void print_numbers(int n, …);//`n` is a fixed parameter indicating the number of subsequent variable parameters

Then define a variable of type va_list, which will be used to access the variable argument list. va_list is a type used to declare a variable that will traverse the variable argument list. You can think of it as a pointer to the argument list. The va_start macro is used to initialize the va_list variable so it can start traversing the variable argument list. It is usually used with the last fixed parameter of the function to determine the starting position of the variable argument list. The va_arg macro is used to retrieve the next parameter from the position pointed to by the va_list variable and updates the va_list variable to point to the next parameter.

Example:

Recursive Functions

Question:

You’ve used recursion, right? Do you understand how recursion is implemented? Please explain with an example.

Answer:

A recursive function is one that calls itself directly or indirectly within its body.

Recursion solves a complex problem by breaking it down into smaller sub-problems, each of which is an instance of the recursive call, until the recursion termination condition is reached.

Thus, recursion requires attention to two conditions:

The first is the termination condition for the recursion. When this condition is met, the recursive calls will stop, thus avoiding infinite loops;

The second is the part where the function calls itself.

Example:

Embedded C/C++ Interview Notes - A Collection from Major Companies

2. C++ Programming Questions

2.

Embedded C/C++ Interview Notes - A Collection from Major Companies

Because C++ is used slightly less in the embedded domain compared to C, and in some scenarios, C++ is often used with C syntax, it generally ranks second. Commonly asked foundational questions in C++ primarily revolve around object-oriented programming.

Common questions mainly include the differences and characteristics between different languages, followed by object-oriented language features, and finally, the usage techniques of constructors, destructors, and virtual functions.

C++ consists of three important parts: the core language, the standard library, and the standard template library.

The core language provides all the building blocks, including variables, data types, and constants, etc.

The C++ standard library offers a large number of functions for operating on files, strings, etc.

The standard template library (STL) provides numerous methods for manipulating data structures, etc.

1

Advantages and Disadvantages of C/C++/Java

The advantages and characteristics of C, C++, and Java programming languages are as follows:

The advantages of C language: efficiency, low-level control, simplicity, and portability; characterized as a procedural programming language that can directly access physical addresses.

The advantages of C++ over C: object-oriented programming, standard library, and template support;

The advantages of C++ over Java: performance advantages, direct memory access, and hardware operations;

The advantages of Java: cross-platform, and rich libraries and frameworks; Java is characterized by its rich API.

Due to the above characteristics, in embedded programming, the HAL layer and drivers primarily use C language; the framework layer software mainly uses C++; while the APP layer consists entirely of Java code (this part is handled by application developers).

2

C++ Object Orientation

C++ fully supports object-oriented programming, including the four major characteristics of object-oriented development:

Encapsulation: Data and methods are combined, hiding implementation details from the outside, only exposing the interfaces provided to the outside. This can improve security, reliability, and flexibility.

Inheritance: A new class is derived from an existing class, inheriting the properties and methods of the existing class, and can extend or modify these properties and methods. This enhances code reusability and extensibility. Inheritance allows us to define a class based on another class. You only need to specify that the newly created class inherits the members of an existing class. The existing class is called the base class, and the newly created class is called the derived class.

class Dog : public Animal // Derived class

Multiple inheritance: A subclass can have multiple parent classes, inheriting the features of multiple parent classes.

// Derived classclass Rectangle: public Shape, public PaintCost

Polymorphism: The same operation can have different interpretations and implementations when applied to different objects. It can be achieved through interfaces or inheritance, improving code flexibility and readability.

Abstraction: Extracting common features from specific instances to form abstract classes or interfaces, facilitating code reuse and extension. Abstract classes and interfaces allow programmers to focus on high-level design and business logic without worrying about low-level implementation details.

3

Differences Between C++ and C

References Can Be Easily Confused with Pointers

There are three main differences between them:

There are no null references. A reference must be connected to valid memory.

Once a reference is initialized to an object, it cannot be pointed to another object. Pointers can point to another object at any time.

References must be initialized upon creation. Pointers can be initialized at any time.

Namespaces

Namespaces can serve as additional information to distinguish functions, classes, variables, etc., with the same name in different libraries.

A folder (directory) can contain multiple folders, but each folder cannot have the same file name; however, files in different folders can have the same name.

Dynamic Memory

In C++, the special operator is used to allocate memory on the heap for a variable of a given type at runtime, returning the address of the allocated space. This operator is the new operator.

If you no longer need the dynamically allocated memory space, you can use the delete operator to remove the memory allocated by the new operator.

Classes and Objects

A class is a core feature of C++, often referred to as a user-defined type. Classes specify the form of objects, containing data representations and methods for processing data. The data and methods within a class are called class members.

Member Functions of Classes:

Member functions can be defined within the class definition or separately using the scope resolution operator ::.

Access Modifiers for Classes:

The keywords public, private, and protected are called access modifiers.

Constructors and Destructors of Classes

A constructor is a special member function of a class that is executed whenever a new object of the class is created.

A destructor is executed when an object is destroyed.

Parameterized Constructors

Line::Line(double len)

A destructor of a class is a special member function that is executed whenever an object created is deleted.

Friend Functions

A friend function of a class is defined outside the class but has access to all private and protected members of the class. Although the prototype of the friend function appears in the class definition, the friend function is not a member function.

Friends can be a function, called a friend function; friends can also be a class, called a friend class; in this case, the entire class and all its members are friends.

To declare a function as a friend of a class, the keyword friend must be used before the function prototype in the class definition.

this Pointer

The this pointer is mainly used within a class’s member function to access the members (including member variables and member functions) of the class.

Inside a class’s member function, you can directly use the class’s member variables and member functions without explicitly accessing them through the this pointer. However, in some cases, using the this pointer explicitly can make the code clearer or solve specific programming issues (such as returning a reference or pointer to the current object).

Class Pointers

To access members of a class pointer, you need to use the member access operator ->, just like accessing members of a struct.

Box *ptrBox; // Declare a pointer to a class

// Save the address of the first object

ptrBox = &Box1;

// Now try to use the member access operator to access members

cout << “Volume of Box1: ” << ptrBox->Volume() << endl;

Static Members of Classes

Static variables of a class are re-declared externally using the scope resolution operator ::, and static functions can be accessed using the class name and the scope resolution operator ::.

int Box::objectCount = 0; // Initialize the static member of class Box

Operator Overloading and Function Overloading

Overloading declarations refer to a declaration that has the same name as a previously declared function or method in that scope, but with a different parameter list and definition (implementation).

Operator Overloading

An overloaded operator is a function with a special name, where the function name consists of the keyword operator followed by the operator symbol to be overloaded.

Box operator+(const Box&);

Templates

Using generics to define functions, where the generics can be replaced with specific types (like int or double). By passing types as parameters to the template, the compiler can automatically generate functions for that type.

Examples of Using Polymorphism and Virtual Functions

Embedded C/C++ Interview Notes - A Collection from Major Companies

3. Professional Field Questions

Embedded C/C++ Interview Notes - A Collection from Major Companies

For example, in the imaging embedded field where I work, in addition to embedded knowledge, basic imaging knowledge is also required; if engaging in audio embedded, basic audio knowledge is necessary;

If interviewing for algorithm integration software, one also needs to understand the basic concepts of commonly used algorithms; in embedded AI, one needs to understand the basics and deployment methods of AI algorithms.

How to Obtain the Complete Version

The content I organized is comprehensive, but the full version with code examples is too lengthy. To obtain the complete version with code examples, follow my public account, or contact me via WeChat for random rewards, and you can get the complete version.

Embedded C/C++ Interview Notes - A Collection from Major Companies

The questions are quite disorganized and mixed. They are written based on my personal experiences from interviews at several top companies in the embedded sector. The later organization and refinement took the entire holiday season. I hope you guys will like, follow, and give back.

After the new year, we enter a small peak of job interviews, known as the “golden three silver four,” wishing you all the best in achieving your desires.

Embedded C/C++ Interview Notes - A Collection from Major Companies
Embedded C/C++ Interview Notes - A Collection from Major Companies

Leave a Comment