35+ Top Interview Questions for C Programming (2023) - IQCode

The Basics of C Language and Interview Questions

C is a highly popular computer language due to its structure, machine-independent feature, and overall high-level abstraction. It was initially developed for the UNIX operating system, which is one of the most commonly used network operating systems currently, and powers the internet data superhighway.

In this article, we'll take a look at potential questions you might encounter in a C interview, designed for fresher, intermediate, and advanced levels.

Basic Interview Questions on C

1. What is the value of the expression "5["ABXDEF"]"?

Code:

// The expression can be simplified as "ABXDEF"[5] // The array index starts from 0 in C language, so "ABXDEF"[5] is the 6th character // In this case, the 6th character is "F", therefore, the output is "F" printf("%c", "ABXDEF"[5]);

The expected output is "F".

Understanding Built-in Functions in C

In C programming language, a built-in function is a function that is already defined in the C standard library and can be used directly in a C program without requiring an explicit declaration. These functions are pre-defined and can perform common tasks, such as mathematical calculations, string manipulations, and input/output operations, among others. Some examples of commonly used built-in functions in C are printf(), scanf(), abs(), sqrt(), and strlen(). The use of built-in functions can greatly simplify and speed up the development of C programs.H3. Usage of #line in C

In C programming, the #line directive is used to specify the line number and/or filename for a particular line of code in the compiled output. This information is used by the debugger to map the machine code back to the original source code. The syntax for #line directive is as follows:

#line linenumber "filename"

Here, linenumber is the line number in the file where the directive is written, and filename is an optional parameter that specifies the name of the file. If no filename is provided, the name of the current file is used.

Note that the #line directive does not affect the compilation process or the generated code, but only provides information to the debugger. It is typically used in conjunction with other debugging tools to help developers diagnose issues in their code.

Converting a String to a Number in JavaScript

In JavaScript, you can convert a string to a number using the `parseInt()` or `parseFloat()` methods. `parseInt()` converts a string to an integer, while `parseFloat()` converts a string to a floating-point number. Here is an example:


// Parsing an integer
let strInt = "123";
let numInt = parseInt(strInt);
console.log(numInt); // Output: 123

// Parsing a floating-point number
let strFloat = "3.14";
let numFloat = parseFloat(strFloat);
console.log(numFloat); // Output: 3.14

It's important to note that if the string contains non-numeric characters, the methods will only return the numeric part of the string. For example:


let strMixed = "123abc";
let numMixed = parseInt(strMixed);
console.log(numMixed); // Output: 123

If you need to convert a string that represents a hexadecimal number to a decimal number, you can use the `parseInt()` method and specify the base (16 for hexadecimal). For example:


let hexStr = "ff";
let decimalNum = parseInt(hexStr, 16);
console.log(decimalNum); // Output: 255


Converting a Number to a String in JavaScript

In JavaScript, you can convert a number to a string using the toString() method.

Example:

javascript
let number = 42;
let str = number.toString(); // "42"

Alternatively, you can use the String() function to convert a number to a string.

Example:

javascript
let number = 42;
let str = String(number); // "42"

Reasons why C does not support function overloading

C language does not support function overloading because:

1. C code uses a linker to link all function calls, which requires unique names for each function. Function overloading creates multiple functions with the same name, which cannot be linked by the linker.
2. C language is a procedural programming language, which means the function name itself identifies the processing performed by the function. Overloading leads to confusion and makes code difficult to understand and maintain.

Therefore, C does not support function overloading as it goes against the fundamental values and architecture of the language.

Difference between global int and static int declaration

In C programming, a global int variable can be accessed and modified from any part of a program, whereas a static int variable is limited in scope to the function or block in which it is declared. Additionally, a static int will retain its value between function calls, while a global int will have its value reset to 0 each time the program runs.

Difference between const char* p and char const* p

In C++, both `const char* p` and `char const* p` are equivalent and declare a pointer to a constant character or a constant string. They both indicate that the data pointed to by `p` should not be modified.

The difference is in the way they are read. In `const char* p`, the `const` keyword applies to the character being pointed to. On the other hand, in `char const* p`, the `const` keyword applies to the pointer itself. However, in practice, both declarations are used interchangeably and the difference is mostly a matter of personal preference.

Why does N++ execute faster than N+1?

Code optimization and execution speed depend on multiple factors, including the specific code implementation and hardware specifications. It is not necessarily true that N++ will always execute faster than N+1. Without specific information about the code and hardware in question, it is difficult to determine the reason for any potential performance differences.

Advantages of Macros over Functions

Macros are a powerful feature in C programming that provide several advantages over functions:

  1. Macros are faster than functions because they are executed at compile-time rather than run-time.
  2. Macros can be used to define constants, which cannot be changed during the program execution.
  3. Macros can be used to shorten frequently used code, making the code easier to read and maintain.
  4. Macros can take any number of arguments, unlike functions which have a fixed number of parameters.
  5. Macros can provide more flexibility in certain situations, such as when the program needs to access hardware directly.

However, macros can also have some disadvantages. For example, since macros are expanded before the code is compiled, any errors in the macro code will not be detected until compilation. Additionally, macros can make debugging more difficult, because they do not have their own symbol table.

C Intermediate Interview Questions

11.

Can you explain the various types of decision control statements in C?

In C programming, decision control statements allow the program to make decisions based on specific conditions. There are three types of decision control statements:

  • if

    statement: This statement is used when you want to execute a particular block of code only if a given condition is true.

  • if-else

    statement: This statement is used when you want to execute one block of code if a given condition is true and execute another block of code if the same condition is false.

  • switch

    statement: This statement allows you to specify several alternative blocks of code to be executed, depending on the value of the expression provided.

These statements are vital in programming as they help in making the code more flexible, efficient, and easy to read.

Difference between Struct and Union in C

In C programming language, a struct is a user-defined data type that groups different variables of different data types under a single name. On the other hand, union is also a user-defined data type that is similar to struct but it only uses memory size enough to hold the largest member of its definition.

The main difference between struct and union is that, in a struct, each member has its own memory location whereas, in a union, all members share the same memory location. Therefore, modifying any member of a union changes the value of other members which can be unexpected behavior.

Another difference is that a struct can store different types of data, while a union can only store one type of data at a time. Structs are used when we need to group different data types together in a single unit, while unions are used when we want to save memory by using the same memory location for different data types.

In summary, a struct is used to group different data types under a single name while a union is used to store a single data type in a variable which saves memory.

Call by Reference in Functions

In programming, call by reference is a way of passing arguments to a function that allows the function to modify the original value of the argument. This is done by passing the memory location of the argument rather than its value.

When using call by reference, any changes made to the parameter inside the function also affect the original argument outside the function. This is in contrast to call by value, where a copy of the argument is passed to the function and any changes made to the parameter inside the function do not affect the original argument.

Call by reference can be useful when working with large or complex data structures, as it allows the function to modify the original data directly rather than creating a new copy. However, it also requires careful management to avoid unintended side effects.

PASS BY REFERENCE IN FUNCTIONS

In programming, pass by reference is a method of passing parameters to a function. When a parameter is passed by reference, the function receives an address that refers to the original variable rather than a copy of its value. This means any changes made to the parameter inside the function will affect the original variable outside the function.

Passing parameters by reference can be useful when working with large data structures, as it reduces the amount of memory needed to store multiple copies of the data. However, it can also lead to unexpected side effects if not used carefully.

What is a Memory Leak and How to Avoid It?

In programming, a memory leak occurs when a program fails to free up memory that is no longer being used, causing the program to consume more memory over time. This can lead to performance issues, crashes, and even system failures.

To avoid memory leaks, it is important to properly manage memory allocation and deallocation in your code. Here are some tips:

1. Use smart pointers instead of raw pointers to manage memory. 2. Always free up memory after it's no longer needed. 3. Avoid circular references, where objects reference each other and none of them can be deleted. 4. Use a memory profiling tool to detect and fix leaks. 5. Test your code thoroughly to catch and fix any memory leaks before they become a problem.

By following these best practices and being mindful of how memory is being used in your code, you can avoid memory leaks and create more stable and efficient programs.

DYNAMIC MEMORY ALLOCATION IN C AND ITS FUNCTIONS

Dynamic memory allocation allows a program to request memory while it is running. This lets a program be flexible and allocate memory as needed, rather than at compile-time. In C, we have two functions for dynamic memory allocation:

- malloc(size_t size): This function allocates a block of memory of specified size and returns a pointer of type void which can be cast to any pointer type.

- free(void *ptr): This function deallocates the memory block pointed by the pointer ptr.

Other functions that can be used for dynamic memory allocation in C include realloc(), calloc() and alloca(). These functions provide various capabilities for allocating and deallocating memory based on the requirements of your program.

Reasons to avoid using gets() function and its workaround

The gets() function is used to read a line of characters from standard input and store it into an array until a newline character is encountered. There are several reasons why the gets() function should be avoided:

  • It does not perform any bounds checking, so it can easily result in a buffer overflow vulnerability if the input exceeds the size of the buffer.
  • It cannot distinguish between a properly formatted input and an input that contains errors.
  • It is not thread-safe, which means that it can have unpredictable behavior when used in a multi-threaded application.

A better alternative to gets() function is to use fgets() function, which allows us to set a limit on the maximum number of characters to be read. This helps to avoid buffer overflow vulnerabilities. Also, fgets() returns NULL if it encounters an error or the end of the file, which helps to detect errors. Here is an example:


char input[100];
fgets(input, sizeof(input), stdin);

This code reads a line of input from standard input and stores it into an array called input. The sizeof(input) argument ensures that fgets() will not read more than 100 characters, which is the maximum size of the input array.

Difference between #include "..." and #include <...>

In C and C++, #include directive is used to include header files in the source code. There are two ways to use the #include directive:

  • Using double quotes ("") around the header file name. For example, #include "header.h".
  • Using angle brackets (<>) around the header file name. For example, #include <header.h>.

The main difference between these two ways of including header files is the location where the preprocessor looks for the file.

  • When we use double quotes (""), the preprocessor looks for the file first in the same directory where the source file is located. If not found in the current directory, it will search the standard library(include paths) directories.
  • When we use angle brackets(<>), the preprocessor looks for the file in the standard library(include paths) directories.

It is recommended to use angle brackets(<>) for standard library header files and double-quotes("") for user-defined header files.

Dangling Pointers and their Difference from Memory Leaks

Dangling pointers are pointers that point to a space in the heap that has already been deallocated, leading to undefined behavior when dereferenced. In other words, they point to memory that has been freed and could contain garbage values or might have been allocated to another entity.

On the other hand, memory leaks are slightly different and occur when there is no longer any reference to a space in the heap, and, as a result, the space remains unclaimed until the program terminates. In contrast to dangling pointers that point to invalid memory, memory leaks point to valid memory, but they become unusable after some time.

In summary, the primary difference between the two is what they point to: dangling pointers point to invalid memory, while memory leaks point to valid memory but are unusable due to lack of a reference or pointer to them. Both of these scenarios can cause problems in software, which is why developers must keep a keen eye on their memory handling practices.

Difference between 'G' and "G" in C

In C, 'G' is a character literal and "G" is a string literal.

A character literal represents a single character and is enclosed in single quotes (' '). For example:

char c = 'G';

On the other hand, a string literal represents a sequence of characters and is enclosed in double quotes (" "). For example:

char str[] = "G";

Note that in C, single quotes are used for character literals and double quotes are used for string literals. Using the wrong type of quote can result in a syntax error.

C Interview Questions for Experienced

One of the interview questions for an experienced C programmer could be:

Can you explain to me how to determine if a linked list is circular?

One way to accomplish this is by using two pointers- a slow pointer and a fast pointer. The slow pointer moves one node at a time, while the fast pointer moves two nodes at a time. If the list is circular, then at some point, the fast pointer will catch up to the slow pointer. If the list is not circular, then the fast pointer will eventually reach the end of the list and the loop can terminate.


int isCircular(node *head) {
    node *slow = head; 
    node *fast = head;

    while(fast != NULL && fast->next != NULL) {   // traverse the list with two pointers
        slow = slow->next; 
        fast = fast->next->next; 
        if(slow == fast) {   // if the fast pointer catches up to the slow pointer, the list is circular
            return 1;
        }
    } 
    return 0;   // if the end of the list is reached and the fast pointer has not caught up, the list is not circular
}


The Purpose of Semicolons in Programming Statements

In programming, semicolons (;) are typically used as statement terminators. They indicate the end of a line of code and signal to the interpreter or compiler that it's time to move on to the next statement. Semicolons are often required in languages like JavaScript, Java, and C++, but not always necessary in languages such as Python. It's good coding practice to use semicolons as statement terminators to ensure that your code is interpreted correctly and to prevent errors from occurring.

Distinguishing Source Code from Object Code

When programming, it is important to understand the difference between source code and object code. Source code refers to the human-readable code that programmers write in a high-level programming language. Object code, on the other hand, refers to the machine-readable version of that code after it has been compiled.

 
// Example of source code in C++
#include <iostream>

int main()
{
    std::cout << "Hello, world!";
    return 0;
}

Once compiled, this source code will generate object code that can be executed by the computer's processor. Understanding the difference between these two types of code is essential for troubleshooting and debugging software programs.

Header Files and Their Uses in C Programming

Header files in C programming are files that contain function prototypes, variable declarations, and macro definitions that are needed for a program to run. Header files are included in a C program using the `#include` preprocessor directive.

The main purpose of header files is to provide an organized way of declaring the various functions and variables that are used in a program. This helps in avoiding duplication of code and makes the program more modular and maintainable.

Some common header files in C programming include: `stdio.h` for input-output operations, `stdlib.h` for dynamic memory allocation, `math.h` for mathematical functions, and `string.h` for string operations.

In summary, header files are essential components of a C program that make it easier to declare functions and variables, avoid code duplication, and make the program more modular and maintainable.

Usage of "void" keyword in a function

The "void" keyword is used in functions to indicate that the function does not return a value. This means that the function performs some task or operation, but does not produce any result that can be assigned to a variable or used in an expression. For example, a function that simply displays a message to the user on the screen might be declared like this:

void showMessage(string message) {
  cout << message << endl;
}

Here, the "void" keyword indicates that the "showMessage" function does not return a value. It takes a string parameter called "message" and displays this message on the screen using the "cout" statement.

DYNAMIC DATA STRUCTURES

Dynamic data structures refer to structures that can change in size during run time. Unlike static data structures, where the size is fixed at the time of creation, dynamic structures can be resized, added or deleted while the program is running. Examples of dynamic data structures include linked lists, stacks, queues, trees, and graphs. These structures are useful in situations where the size of data is not known beforehand or when there is a possibility of adding or removing data during program execution.

Adding two numbers without using the addition operator

Here is a python function that adds two numbers without using the '+' operator. It uses the bitwise operators '&', '|' and '^', and bit shifts to perform the addition operation.


def add_numbers(num1, num2):
    while (num2 != 0):
        carry = num1 & num2
        num1 = num1 ^ num2
        num2 = carry << 1
    return num1

The 'add_numbers' function takes two integer parameters 'num1' and 'num2', and returns their sum. It uses a while loop to repeatedly calculate the carry and the sum of two numbers until carry becomes zero. Inside the while loop, carry is calculated using the bitwise AND operator '&', while the sum is calculated using the bitwise XOR operator '^'. Finally, 'num2' is left-shifted by 1 bit to get the next carry. The loop continues until the carry becomes zero, and the final sum is returned.

Here is an example of how to use the 'add_numbers' function:


# Add 2 and 5
sum = add_numbers(2, 5)
print("Sum is:", sum)

This will output:


Sum is: 7


Subtracting Two Numbers Without Using the Subtraction Operator

To subtract two numbers without using the subtraction operator, we can use the concept of the two's complement. Here's how:


int subtract(int x, int y) {
    // convert y to its two's complement
    y = ~y + 1;

    // add x and y
    return x + y;
}

By converting y to its two's complement and adding it to x, we are essentially subtracting y from x. This works because the two's complement of a number is the same as flipping all the bits and adding one.

For example, the two's complement of 5 (0101) is -5 (1011) since we flip all the bits and add one. So, if we want to subtract 5 from 10, we can convert 5 to its two's complement (-5) and add it to 10 to get 5 as the result:


int result = subtract(10, 5); // result = 5


Multiplying an Integer by 2 Without Using Multiplication Operator

To multiply an integer by 2 without using the multiplication operator, we can simply perform a left shift operation on the binary representation of the number by one position. This is equivalent to multiplying the original number by 2.


int multiplyByTwo(int num) {
  return num << 1; // Left shift operation
}

The left shift operation shifts all the bits of the number to the left by one position, effectively multiplying by 2.

Checking if a number is even or odd without arithmetic or relational operators

To check if a number is even or odd without using arithmetic or relational operators, we can make use of the bit-wise AND operator. Every even number's last bit is 0, whereas every odd number's last bit is 1. By applying the bit-wise AND operator with 1 on the given number, we can determine if it is even or odd.


    // Function to check if a number is even or odd without arithmetic or relational operators
    function checkEvenOrOdd(num) {
      if((num & 1) === 0) {
        return num + " is even";
      } else {
        return num + " is odd";
      }
    }

    // Example usage
    console.log(checkEvenOrOdd(4)); // Output: 4 is even
    console.log(checkEvenOrOdd(7)); // Output: 7 is odd


Reverse a Linked List

Given a singly linked list, the task is to reverse the list.


    class Node { 
        int data; 
        Node next; 
        Node(int d) { 
            data = d; 
            next = null; 
        } 
    } 

    class LinkedList { 
        Node head; 

        /* Function to reverse the linked list */
        Node reverse(Node node) { 
            Node prev = null; 
            Node current = node; 
            Node next = null; 
            while (current != null) { 
                next = current.next; 
                current.next = prev; 
                prev = current; 
                current = next; 
            } 
            node = prev; 
            return node; 
        } 

        /* Function to print linked list */
        void printList(Node node) { 
            while (node != null) { 
                System.out.print(node.data + " "); 
                node = node.next; 
            } 
        } 

        public static void main(String[] args) { 
            LinkedList list = new LinkedList(); 
            list.head = new Node(1); 
            list.head.next = new Node(2); 
            list.head.next.next = new Node(3); 
            list.head.next.next.next = new Node(4); 
            list.head.next.next.next.next = new Node(5); 

            System.out.println("Given Linked list"); 
            list.printList(list.head); 
            list.head = list.reverse(list.head); 
            System.out.println(""); 
            System.out.println("Reversed linked list "); 
            list.printList(list.head); 
        } 
    }

The expected output for the given input "1->2->3->4->5->NULL" is "5->4->3->2->1->NULL".

Check for Balanced Parentheses Using Stack

In this code, we will implement a stack to check whether the given string has balanced parentheses or not. We will use the stack data structure to solve this problem. The open brackets will be pushed into the stack, and when we encounter a closing bracket, we will check if it matches the top of the stack. If it matches, we will pop the top element from the stack, and if it doesn't, we will return False as it indicates that the string has unbalanced parentheses.

def check_parentheses(input_string): stack = [] opening_brackets = ["(", "[", "{"] closing_brackets = [")", "]", "}"] for bracket in input_string: if bracket in opening_brackets: stack.append(bracket) elif bracket in closing_brackets: pos = closing_brackets.index(bracket) if len(stack) > 0 and opening_brackets[pos] == stack[len(stack) - 1]: stack.pop() else: return False if len(stack) == 0: return True else: return False

Let's test our function with some input strings.

print(check_parentheses("()")) print(check_parentheses("()[]{}")) print(check_parentheses("(]")) print(check_parentheses("([)]")) print(check_parentheses("{[]}"))

The output will be:

True True False False True

Finding Nth Fibonacci Number

The following code finds the Nth Fibonacci number, where N is a positive integer.


def fibonacci(n):  # defining function to find the Nth Fibonacci number
    if n <= 1:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2) # recursive call to find the Nth Fibonacci number

# taking input from user
n = int(input("Enter a positive integer: "))

# printing the Nth Fibonacci number
print("The", n,"th Fibonacci number is:", fibonacci(n))


Finding Intersection Node of Two Singly Linked Lists


class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next
        
def getIntersectionNode(headA, headB):
    """
    :type headA: ListNode
    :type headB: ListNode
    :rtype: ListNode
    """
    #if the heads of both lists are equal there is an intersection
    if headA == headB:
        return headA
    
    #the lengths of the two lists are calculated
    currentA = headA
    lengthA = 1
    while currentA.next:
        currentA = currentA.next
        lengthA += 1
    
    currentB = headB
    lengthB = 1
    while currentB.next:
        currentB = currentB.next
        lengthB += 1
        
    #the pointers of the larger list are incremented so that they're aligned
    if lengthA > lengthB:
        for i in range(lengthA - lengthB):
            headA = headA.next
    else:
        for i in range(lengthB - lengthA):
            headB = headB.next
            
    #points are incremented until there's a match
    while headA != headB:
        headA = headA.next
        headB = headB.next
    
    return headA

This program finds the node at which the intersection of two singly linked lists begins. It does so by first calculating the lengths of both linked lists. Then, it aligns the pointers of both lists by incrementing the pointers of the larger list. Finally, it increments the pointers of both lists until there's a match in the nodes. If there isn't a match, the function will return None.

Merging two sorted linked lists

Here's an implementation of a function that merges two sorted linked lists into a single linked list in ascending order.


/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        // Create a dummy node that will act as the head of our merged list
        ListNode* dummy = new ListNode(-1);
        // Create a pointer to the current tail of our merged list
        ListNode* tail = dummy;

        while (l1 != NULL && l2 != NULL) {
            if (l1->val <= l2->val) {
                // Add l1 to the merged list
                tail->next = l1;
                l1 = l1->next;
            } else {
                // Add l2 to the merged list
                tail->next = l2;
                l2 = l2->next;
            }
            // Advance the tail pointer
            tail = tail->next;
        }

        // Add any remaining nodes from l1 or l2 to the end of the merged list
        tail->next = l1 != NULL ? l1 : l2;

        return dummy->next;
    }
};

The function creates a dummy node to act as the head of the merged list and a tail pointer initially pointing to the dummy node. It then iterates through both input lists, adding the smallest node to the merged list at each iteration until one of the lists is fully processed. Finally, any remaining nodes from the unprocessed list are added to the end of the merged list and the function returns the next node after the dummy node, effectively skipping over it.

Technical Interview Guides

Here are guides for technical interviews, categorized from introductory to advanced levels.

View All

Best MCQ

As part of their written examination, numerous tech companies necessitate candidates to complete multiple-choice questions (MCQs) assessing their technical aptitude.

View MCQ's
Made with love
This website uses cookies to make IQCode work for you. By using this site, you agree to our cookie policy

Welcome Back!

Sign up to unlock all of IQCode features:
  • Test your skills and track progress
  • Engage in comprehensive interactive courses
  • Commit to daily skill-enhancing challenges
  • Solve practical, real-world issues
  • Share your insights and learnings
Create an account
Sign in
Recover lost password
Or log in with

Create a Free Account

Sign up to unlock all of IQCode features:
  • Test your skills and track progress
  • Engage in comprehensive interactive courses
  • Commit to daily skill-enhancing challenges
  • Solve practical, real-world issues
  • Share your insights and learnings
Create an account
Sign up
Or sign up with
By signing up, you agree to the Terms and Conditions and Privacy Policy. You also agree to receive product-related marketing emails from IQCode, which you can unsubscribe from at any time.