C Compound Data Types Practice Problems
C Compound Data Types Practice Problems: A Comprehensive Tutorial
Compound data types in C—arrays, pointers, strings, structures, and unions—are built on top of the primitive types and enable programmers to model real-world entities. Fluency with them is non-negotiable for systems programming, embedded work, and technical interviews.
This tutorial walks through 19 graded practice problems organized into Easy and Medium tiers. Each problem is a complete, portable C99/C11 program with inline comments and precise expected output.
Conceptual Introduction & Fundamentals
What are Compound Data Types?
Compound data types are constructed from primitive types to model more complex data:
| Type | Description |
|---|---|
| Array | Contiguous collection of same-type elements |
| Pointer | Variable storing a memory address |
| String | Null-terminated array of char |
| Struct | Grouping of different-typed fields |
| Union | Overlapping storage for multiple types |
Why Practice Them?
- Real-world modeling: Records, buffers, packets, matrices.
- Foundation for data structures: Linked lists, trees, hash tables.
- Interview staples: Almost every C interview problem uses one or more.
- Bug magnet: Pointer arithmetic, string termination, struct padding are common sources of subtle bugs.
The Concepts You Must Know
- Array decay: Arrays pass to functions as pointers.
- Pointer arithmetic:
p + 1advances bysizeof(*p). - Null termination: C strings end with
'\0'. - Struct layout: Fields may be padded for alignment.
- Union semantics: Only the last-written member is valid.
Syntax & Core Mechanics
Array Basics
c
int arr[5] = {1, 2, 3, 4, 5}; size_t n = sizeof(arr) / sizeof(arr[0]); /* number of elements */ Pointer Basics
c
int x = 10; int *p = &x; *p = 20; /* modify x */ printf("%p\n", (void*)p); /* print address */ String Basics
c
char name[32]; if (fgets(name, sizeof(name), stdin) == NULL) { /* error */ } name[strcspn(name, "\n")] = '\0'; /* strip newline */ Struct Basics
c
struct Point { int x, y; }; struct Point p = {1, 2}; struct Point *pp = &p; printf("%d\n", pp->x); Union Basics
c
union Value { int i; float f; }; Full Practical Code Example with Explanation
Below are all 19 problems, grouped by difficulty.
Easy Problems
Problem 1: Size of an Array
c
#include <stdio.h> int main(void) { int arr[] = {1, 2, 3, 4, 5}; size_t total = sizeof(arr); size_t elem = sizeof(arr[0]); size_t count = total / elem; printf("Size: %zu bytes, Elements: %zu\n", total, count); return 0; } Expected output:
text
Size: 20 bytes, Elements: 5
Problem 2: Print Array
c
#include <stdio.h> void printArray(const int arr[], int n) { for (int i = 0; i < n; i++) printf("%d ", arr[i]); printf("\n"); } int main(void) { int arr[] = {10, 20, 30, 40, 50}; printArray(arr, 5); return 0; } Expected output:
text
10 20 30 40 50
Problem 3: Print Array in Reverse
c
#include <stdio.h> void printReverse(const int arr[], int n) { for (int i = n - 1; i >= 0; i--) printf("%d ", arr[i]); printf("\n"); } int main(void) { int arr[] = {10, 20, 30, 40, 50}; printReverse(arr, 5); return 0; } Expected output:
text
50 40 30 20 10
Problem 4: Array Insert at End
c
#include <stdio.h> void insertAtEnd(int arr[], int *n, int capacity, int value) { if (*n >= capacity) return; /* caller must check */ arr[(*n)++] = value; } int main(void) { int arr[10] = {1, 2, 3}; int n = 3; insertAtEnd(arr, &n, 10, 4); insertAtEnd(arr, &n, 10, 5); for (int i = 0; i < n; i++) printf("%d ", arr[i]); printf("\n"); return 0; } Expected output:
text
1 2 3 4 5
Problem 5: Count Smaller Than X
c
#include <stdio.h> int countSmaller(const int arr[], int n, int x) { int count = 0; for (int i = 0; i < n; i++) if (arr[i] < x) count++; return count; } int main(void) { int arr[] = {10, 5, 20, 15, 25}; printf("%d\n", countSmaller(arr, 5, 20)); /* 2 */ return 0; } Expected output:
text
2
Problem 6: Sum of All Array Elements
c
#include <stdio.h> long long arraySum(const int arr[], int n) { long long sum = 0; for (int i = 0; i < n; i++) sum += arr[i]; return sum; } int main(void) { int arr[] = {1, 2, 3, 4, 5}; printf("%lld\n", arraySum(arr, 5)); return 0; } Expected output:
text
15
Problem 7: Largest Element in Array
c
#include <stdio.h> int findLargest(const int arr[], int n) { if (n <= 0) return 0; int max = arr[0]; for (int i = 1; i < n; i++) if (arr[i] > max) max = arr[i]; return max; } int main(void) { int arr[] = {3, 7, 1, 9, 4}; printf("%d\n", findLargest(arr, 5)); return 0; } Expected output:
text
9
Medium Problems
Problem 8: Increment Variable Using Pointer
c
#include <stdio.h> void increment(int *p) { if (p != NULL) (*p)++; } int main(void) { int x = 42; increment(&x); printf("%d\n", x); return 0; } Expected output:
text
43
Problem 9: Print Address of Variable
c
#include <stdio.h> int main(void) { int x = 10; int *p = &x; printf("Address of x: %p\n", (void*)&x); printf("Value of p: %p\n", (void*)p); printf("Value of x via pointer: %d\n", *p); return 0; } Expected output (addresses vary):
text
Address of x: 0x7ffc1234 Value of p: 0x7ffc1234 Value of x via pointer: 10
Problem 10: Array Insert at Index
c
#include <stdio.h> int insertAtIndex(int arr[], int n, int capacity, int idx, int value) { if (idx < 0 || idx > n || n >= capacity) return -1; for (int i = n; i > idx; i--) arr[i] = arr[i - 1]; arr[idx] = value; return n + 1; } int main(void) { int arr[10] = {1, 2, 3, 5}; int n = 4; n = insertAtIndex(arr, n, 10, 3, 4); for (int i = 0; i < n; i++) printf("%d ", arr[i]); printf("\n"); return 0; } Expected output:
text
1 2 3 4 5
Problem 11: First Repeating Element
c
#include <stdio.h> #include <stdlib.h> #define TABLE_SIZE 1024 typedef struct { int *keys; int *seen; int capacity; } IntSet; static unsigned int hashFunc(int key, int cap) { return ((unsigned int)key) % (unsigned int)cap; } IntSet *setCreate(int cap) { IntSet *s = malloc(sizeof(IntSet)); if (s == NULL) return NULL; s->keys = malloc((size_t)cap * sizeof(int)); s->seen = calloc((size_t)cap, sizeof(int)); if (s->keys == NULL || s->seen == NULL) { free(s->keys); free(s->seen); free(s); return NULL; } s->capacity = cap; return s; } void setFree(IntSet *s) { if (s) { free(s->keys); free(s->seen); free(s); } } int setInsert(IntSet *s, int key) { unsigned int idx = hashFunc(key, s->capacity); int start = (int)idx; while (s->seen[idx]) { if (s->keys[idx] == key) return 0; idx = (idx + 1) % (unsigned int)s->capacity; if ((int)idx == start) return -1; } s->keys[idx] = key; s->seen[idx] = 1; return 1; } int firstRepeating(const int arr[], int n) { IntSet *s = setCreate(TABLE_SIZE); if (s == NULL) return -1; int result = -1; for (int i = 0; i < n; i++) { if (!setInsert(s, arr[i])) { result = arr[i]; break; } } setFree(s); return result; } int main(void) { int arr[] = {10, 5, 3, 4, 3, 5, 6}; printf("%d\n", firstRepeating(arr, 7)); return 0; } Expected output:
text
3
Problem 12: Reverse String
c
#include <stdio.h> #include <string.h> void reverseString(char s[]) { int i = 0, j = (int)strlen(s) - 1; while (i < j) { char tmp = s[i]; s[i] = s[j]; s[j] = tmp; i++; j--; } } int main(void) { char s[] = "Hello"; reverseString(s); printf("%s\n", s); return 0; } Expected output:
text
olleH
Problem 13: Anagram Check
c
#include <stdio.h> #include <string.h> #include <stdbool.h> bool isAnagram(const char *a, const char *b) { if (strlen(a) != strlen(b)) return false; int count[256] = {0}; for (int i = 0; a[i]; i++) count[(unsigned char)a[i]]++; for (int i = 0; b[i]; i++) { if (--count[(unsigned char)b[i]] < 0) return false; } return true; } int main(void) { printf("%s\n", isAnagram("listen", "silent") ? "true" : "false"); printf("%s\n", isAnagram("hello", "world") ? "true" : "false"); return 0; } Expected output:
text
true false
Problem 14: Struct Area (Rectangle)
c
#include <stdio.h> struct Rect { double width, height; }; double area(struct Rect r) { return r.width * r.height; } int main(void) { struct Rect r = {4.5, 3.0}; printf("%.2f\n", area(r)); return 0; } Expected output:
text
13.50
Problem 15: Add Two Complex Numbers
c
#include <stdio.h> struct Complex { double real, imag; }; struct Complex addComplex(struct Complex a, struct Complex b) { struct Complex c; c.real = a.real + b.real; c.imag = a.imag + b.imag; return c; } int main(void) { struct Complex x = {2.0, 3.0}; struct Complex y = {4.0, -1.0}; struct Complex z = addComplex(x, y); printf("%.2f + %.2fi\n", z.real, z.imag); return 0; } Expected output:
text
6.00 + 2.00i
Problem 16: Implement Union (Type Punning)
c
#include <stdio.h> #include <stdint.h> union FloatBits { float f; uint32_t bits; }; int main(void) { union FloatBits u; u.f = 3.14f; printf("Float: %f, Bits: 0x%08X\n", u.f, u.bits); return 0; } Expected output (typical):
text
Float: 3.140000, Bits: 0x4048F5C3
Problem 17: Maximum in Struct Array
c
#include <stdio.h> struct Student { int id; int grade; }; struct Student topStudent(const struct Student arr[], int n) { struct Student top = arr[0]; for (int i = 1; i < n; i++) { if (arr[i].grade > top.grade) top = arr[i]; } return top; } int main(void) { struct Student arr[] = {{1, 85}, {2, 92}, {3, 78}}; struct Student top = topStudent(arr, 3); printf("ID %d, Grade %d\n", top.id, top.grade); return 0; } Expected output:
text
ID 2, Grade 92
Problem 18: Self-Referencing Structures
c
#include <stdio.h> #include <stdlib.h> struct Node { int value; struct Node *next; }; int main(void) { /* Build a tiny linked list: 10 -> 20 -> 30 */ struct Node *head = malloc(sizeof(struct Node)); if (!head) return 1; head->value = 10; head->next = malloc(sizeof(struct Node)); if (!head->next) { free(head); return 1; } head->next->value = 20; head->next->next = malloc(sizeof(struct Node)); if (!head->next->next) { free(head->next); free(head); return 1; } head->next->next->value = 30; head->next->next->next = NULL; for (struct Node *p = head; p; p = p->next) printf("%d ", p->value); printf("\n"); /* Free */ while (head) { struct Node *tmp = head; head = head->next; free(tmp); } return 0; } Expected output:
text
10 20 30
Problem 19: Swap Numbers Using a Function
c
#include <stdio.h> void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; } int main(void) { int x = 5, y = 9; swap(&x, &y); printf("x=%d, y=%d\n", x, y); return 0; } Expected output:
text
x=9, y=5
Bonus: 2D World Representation
c
#include <stdio.h> #define ROWS 3 #define COLS 4 int main(void) { char grid[ROWS][COLS] = { {'.', '.', '#', '.'}, {'.', '#', '.', '.'}, {'.', '.', '.', '#'} }; for (int r = 0; r < ROWS; r++) { for (int c = 0; c < COLS; c++) putchar(grid[r][c]); putchar('\n'); } return 0; } Expected output:
text
..#. .#.. ...#
Common Pitfalls, Edge Cases & Memory Considerations
1. Array Bounds
arr[n] is out of bounds for a size-n array. Check bounds before every write.
2. Array Decay
Arrays decay to pointers when passed to functions; sizeof returns pointer size.
c
void f(int arr[]) { size_t s = sizeof(arr); /* pointer size */ } Pass the length explicitly.
3. String Termination
Every C string must end with '\0'. Forgetting this breaks printf("%s", ...) and all str* functions.
4. strcpy Overflow
Use snprintf or strncpy with explicit bounds and manual null termination.
c
strncpy(dest, src, size - 1); dest[size - 1] = '\0';
5. Struct Padding
sizeof(struct) may exceed the sum of member sizes due to alignment. Use sizeof for allocation.
6. Union Field Access
Reading a union member that wasn’t last written is undefined behavior (except for unsigned char aliasing).
7. Pointer Dereferencing
Check for NULL before dereferencing. Free heap allocations exactly once.
8. Aliasing
Two pointers pointing to the same memory can cause subtle bugs in swap or comparison code.
9. Off-by-One in Loops
i <= n iterates n+1 times. Use < when appropriate.
10. Memory Considerations
- Prefer stack arrays for small, short-lived data.
- Use
malloc/freefor large or dynamic data. - Free every allocation; never return pointers to stack data.
- Zero-initialize structs with
{0}when partial initialization is expected.
Summary & Best Practices
Compound data types are the vocabulary of C programming. Mastering them unlocks larger programs, cleaner designs, and better performance.
Key Takeaways
- Arrays are contiguous and index-based; size is not stored.
- Pointers store addresses; arithmetic scales by type size.
- Strings are char arrays terminated by
'\0'. - Structs group different types; watch out for padding.
- Unions overlap storage; access only the last-written member.
Best Practices
- Always pass array length to functions.
- Check every
mallocand free every allocation. - Use
snprintffor formatted string building. - Prefer
sizeoffor array and struct sizes. - Zero-initialize structs when only some fields are set.
- Never access out-of-bounds array elements.
- Cast to
unsigned charfor character processing. - Return structs by value for small ones; use pointers for large.
- Test edge cases: empty arrays, single element, negative numbers.
- Compile with
-Wall -Wextra -std=c11to catch issues early.
By working through these 19 problems, you’ll build the practical fluency required for real C programming.











