C programming MCQs with answers and explanations
Revise C types, operators, loops, functions, arrays and pointers with concept notes and explained questions. Practise reasoning about code for technical placement and GATE CS preparation.
C programming is a procedural, general-purpose programming language developed by Dennis Ritchie at Bell Labs in 1972. It is widely used for system programming, embedded systems, and developing operating systems due to its efficiency and direct hardware control capabilities. C provides low-level memory access through pointers, rich set of built-in operators, and a comprehensive standard library.
Key formulas and rules
10 rulesPointer Arithmetic: *(ptr + i) accesses the element at byte address
Array Indexing Equivalence: arr[i] *(arr + i) *(i + arr) i[arr]
Array Element Count: sizeof(arr) / sizeof(arr[0]) calculates total elements in statically allocated arrays
Structure Padding: due to alignment padding to member boundaries
Bitwise AND & OR: a & b iff both bits are ; a | b if at least one bit is
Bitwise Shifts: a << n (left shift); a >> n (arithmetic right shift for non-negative integers)
Two's Complement Representation: (invert all bits and add )
Dynamic Memory Allocation: malloc(n * sizeof(type)) allocates contiguous uninitialized bytes
String Length: strlen(s) returns the character count up to but excluding the null terminator '\0'
Operator Precedence Hierarchy: Postfix () [] -> Unary ++ -- * & Multiplicative * / % Additive + - Shift << >> Relational < > Equality == != Bitwise & ^ | Logical && || Ternary ?: Assignment =
Key concepts
Pointers and Pointer Arithmetic
A pointer is a variable that stores the memory address of another variable. In C, pointers are declared using the * operator. Key operations include: 1) Address-of (&): Gets the memory address of a variable, 2) Dereference (*): Accesses the value at a pointer's address, 3) Pointer arithmetic: Adding/subtracting integers to pointers moves by multiples of the data type size. For example, if ptr points to an int (4 bytes), ptr+1 actually adds 4 to the address. Void pointers can point to any data type but cannot be dereferenced directly. Function pointers store addresses of functions and enable callbacks. NULL pointer has value 0 and points to nothing.
Arrays and Strings
Arrays in C are contiguous memory blocks storing elements of the same type. Array name acts as a pointer to the first element, so arr[i] is equivalent to *(arr+i). Multi-dimensional arrays are stored in row-major order. Strings are character arrays terminated by '\0' (null character). The %s format specifier reads until whitespace, while %c reads a single character. Common string functions include: strcpy(), strcat(), strlen(), strcmp(), strstr(), strtok(). Important: String literals are stored in read-only memory, so modifying them causes undefined behavior. Character arrays must have space for the null terminator. fgets() is safer than gets() for string input.
Structures and Unions
A structure (struct) is a user-defined data type that groups variables of different types. Memory layout includes padding bytes for alignment optimization. The sizeof a struct may be larger than the sum of its members. Access members using dot operator (.) for structs and arrow operator (->) for struct pointers. A union is similar but all members share the same memory location - only one member can hold a value at a time. The union size is at least the size of its largest member, rounded up to its alignment requirement. Typedef creates aliases for data types, making code cleaner. Nested structures allow structures within structures. Self-referential structures (containing pointers to same type) are essential for linked lists and trees.
Dynamic Memory Management
C provides four functions for dynamic memory: malloc() allocates uninitialized memory, calloc() allocates and zero-initializes, realloc() resizes existing allocation, and free() releases memory. These functions operate on the heap. Always check if allocation returned NULL. Memory leaks occur when allocated memory is not freed. Dangling pointers point to freed memory. Double-free errors cause undefined behavior. Wild pointers are uninitialized pointers. Buffer overflow happens when writing beyond allocated memory. Valgrind and similar tools help detect memory issues. Good practice: Set pointers to NULL after freeing.
Bit Manipulation
C provides six bitwise operators: & (AND), | (OR), ^ (XOR), ~ (NOT), << (left shift), >> (right shift). Left shift by n multiplies by . Right shift divides by for positive numbers (implementation-defined for negatives). Common patterns: Setting a bit (x | (1<<n)), Clearing a bit (x & ~(1<<n)), Toggling a bit (x^{1<<n}), Checking a bit (x & (1<<n)). Bit fields in structures allow compact storage. Bitwise operations are faster than arithmetic and essential for embedded programming, device drivers, and low-level hardware control.
Preprocessor Directives
The C preprocessor runs before compilation. #include inserts header files. #define creates macros - object-like (constants) or function-like (with parameters). Use parentheses around macro parameters to avoid precedence issues. #ifdef, #ifndef, #if, #else, #elif, #endif provide conditional compilation. #pragma gives compiler-specific instructions. Predefined macros: __FILE__, __LINE__, __DATE__, __TIME__, __func__. Header guards (#ifndef/#define/#endif) prevent multiple inclusion. The # operator converts to string, ## concatenates tokens. Macros don't have type checking and can cause multiple evaluation of arguments.
Worked examples
Example 1
What is the output of:
int arr[] = {10, 20, 30, 40, 50};
;
printf("%d ", *(ptr++));
printf("%d ", ++ptr);
printf("%d ", (ptr)++);
Solution
Step 1: ptr is initialized to point to arr[0] (value 10)
Step 2: *(ptr++) uses post-increment, so it prints *ptr (10) first, then ptr moves to arr[1]
Step 3: ++ptr uses pre-increment, so ptr moves to arr[2] first, then prints 30
Step 4: (ptr)++ uses post-increment on the value, so it prints 30 first, then arr[2] becomes 31
Output: 10 30 30
Final array: {10, 20, 31, 40, 50}
Example 2
Find the output:
int , ;
if (x++ > 5 && ++y > 10)
printf("True");
else
printf("False, x=%d, y=%d", x, y);
Solution
Step 1: x++ > 5 evaluates to (5 > 5) which is false, then x becomes 6
Step 2: Due to short-circuit evaluation, ++y > 10 is NOT evaluated because the first condition was false
Step 3: The if condition is false, so the else block executes
Step 4: x is now 6 (incremented), y remains 10 (not incremented)
Output: False, ,
Key concept: && short-circuits - if left operand is false, right operand is not evaluated.
Example 3
What does this code print?
char str[] = "Hello";
char p = str;
while (p) {
printf("%c", p++);
if ((p-1) == 'l') break;
}
Solution
Step 1: p points to 'H' (str[0])
Step 2: Loop 1: *p is 'H' (non-zero, so true), print 'H', then p increments to 'e'
Step 3: Check *(p-1) which is 'H', not 'l', continue
Step 4: Loop 2: *p is 'e', print 'e', p increments to 'l'
Step 5: Check *(p-1) which is 'e', not 'l', continue
Step 6: Loop 3: *p is 'l', print 'l', p increments to 'l'
Step 7: Check *(p-1) which is 'l', matches 'l', break
Output: Hel
The loop prints characters until 'l' is encountered and printed.
Example 4
Analyze the output:
struct Point {int x; char y;};
printf("%lu", sizeof(struct Point));
// Assuming: bytes, char=1 byte
Solution
Step 1: struct Point has two members: int x (4 bytes) and char y (1 byte)
Step 2: Raw size would be bytes
Step 3: However, padding is added for alignment. After char y, 3 padding bytes are added
Step 4: This ensures that if the struct were in an array, each Point starts at a 4-byte boundary
Step 5: Total size = 4 (x) + 1 (y) + 3 (padding) = 8 bytes
Output: 8 (on most systems)
Note: Actual padding depends on compiler and platform, but typically 8 on 32/64-bit systems.
Representative solved questions
See the kind of question in this topic before opening the full practice set.
Question 1
What is the output of:
#include <stdio.h>
int main() {
int , ;
printf("%d", a+++++b);
return 0;
}
Answer: C. Compilation error
ExplanationThe expression a+++++b cannot be parsed as a valid C expression after tokenization, so the program fails to compile. Answer: Compilation error.
Sources and review notes
This is an AISEA-authored practice question.
Review status: accepted · Reviewed 2026-08-13 · structure and answer-key checks, editorial quality checks, duplicate screening
Question 2
What is the output of:
#include <stdio.h>
#define SQUARE(x) x * x
int main() {
int ;
printf("%d", SQUARE(a + 1));
return 0;
}
Answer: B. 11
ExplanationStep 1: The macro SQUARE(x) expands to x * x.
Step 2: SQUARE(a + 1) expands to a + 1 * a + 1 (not (a+1)*(a+1)).
Step 3: Substituting : 5 + 1 * 5 + 1.
Step 4: Due to operator precedence: .
Answer: 11
Sources and review notes
This is an AISEA-authored practice question.
Review status: accepted · Reviewed 2026-08-13 · structure and answer-key checks, editorial quality checks, duplicate screening
Question 3
What is the size of the following structure on a 32-bit system?
struct Node {
char c;
int i;
char d;
};
Answer: C. 12 bytes
ExplanationStep 1: On a 32-bit system, int is typically 4 bytes with 4-byte alignment.
Step 2: char c (1 byte) + 3 bytes padding to align int.
Step 3: int i (4 bytes).
Step 4: char d (1 byte) + 3 bytes padding for structure alignment.
Step 5: Total: bytes.
Answer: 12 bytes
Sources and review notes
This is an AISEA-authored practice question.
Review status: accepted · Reviewed 2026-08-13 · structure and answer-key checks, editorial quality checks, duplicate screening
Common mistakes and useful habits
- Always initialize pointers to NULL or a valid address before use to avoid undefined behavior from wild pointers.
- Use sizeof() instead of hardcoding sizes - it makes code portable across different architectures.
- Check return value of malloc/calloc - if NULL, the allocation failed (out of memory).
- Remember that array parameters in functions decay to pointers - sizeof(array) won't work as expected inside functions.
- Use strncpy or snprintf instead of strcpy to prevent buffer overflows, noting that strncpy does not guarantee NUL-termination if the source length reaches or exceeds the destination buffer size (dest[n-1] = '\0' should be enforced).
- When using bit manipulation with signed integers, be aware that right shift of negative numbers is implementation-defined.
- Use parentheses liberally in macros to avoid operator precedence bugs, especially with arithmetic expressions.
- Free dynamically allocated memory in the reverse order of allocation when possible - it helps catch double-free errors.
- Remember that sizeof(char) is always 1 by definition, but sizeof(int) can vary between platforms.
- In printf, %d is for int, %ld for long, %f for float/double, %c for char, %s for strings, %p for pointers.
Ready to test your understanding?
Work through 100 questions with explanations after each answer.