CoreTechED

Loading

Master C compound data types with 19 practice problems on arrays, pointers, strings, structs, and unions. Full C99 code with edge cases.

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:

TypeDescription
ArrayContiguous collection of same-type elements
PointerVariable storing a memory address
StringNull-terminated array of char
StructGrouping of different-typed fields
UnionOverlapping 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 + 1 advances by sizeof(*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/free for 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

  1. Always pass array length to functions.
  2. Check every malloc and free every allocation.
  3. Use snprintf for formatted string building.
  4. Prefer sizeof for array and struct sizes.
  5. Zero-initialize structs when only some fields are set.
  6. Never access out-of-bounds array elements.
  7. Cast to unsigned char for character processing.
  8. Return structs by value for small ones; use pointers for large.
  9. Test edge cases: empty arrays, single element, negative numbers.
  10. Compile with -Wall -Wextra -std=c11 to catch issues early.

By working through these 19 problems, you’ll build the practical fluency required for real C programming.