CoreTechED

Loading

C Functions Practice Problems: Complete Guide

Master C functions with 11 graded practice problems: print, sum, factorial, prime factorization, recursion, Fibonacci. Full C99 code and edge cases.

C Functions Practice Problems: Complete Guide

C Functions Practice Problems: A Comprehensive Tutorial

Functions are the basic building blocks of C programs. They enhance modularity and code reusability by separating the logic of a particular task from the main code and letting you call it whenever required. This tutorial walks through 11 graded practice problems covering function definition, parameters, return values, and recursion—with complete, portable C99/C11 solutions.

Each problem is self-contained, includes inline comments, and shows the exact expected output.


Conceptual Introduction & Fundamentals

Why Practice Functions?

Functions are where beginner C programmers typically plateau. Writing a few functions isn’t enough—you need to internalize:

  • Parameter passing (by value vs. by pointer).
  • Return values vs. output parameters.
  • Recursion and its call-stack footprint.
  • Decomposition: breaking problems into small, testable units.
  • Interface design: naming, signatures, and boundary conditions.

The Function Toolkit

ConceptPurpose
Function prototypeDeclares the signature to the compiler
Function definitionProvides the implementation
ParametersInput values (copied by default)
Return valueSingle output via return
RecursionA function calling itself
voidUsed for “no value”

Function Signature Template

c

return_type function_name(parameter_list);

Example:

c

int add(int a, int b); /* prototype */ int add(int a, int b) { /* definition */ return a + b; }

Progress Plan

The problems below start simple (print, sum) and advance to recursion and number-theory style tasks.


Syntax & Core Mechanics

Parameters and Return Values

C uses pass by value by default. To modify the caller’s variable, pass a pointer:

c

void increment(int *p) { (*p)++; }

Recursion: Two Requirements

  1. Base case: when the function stops calling itself.
  2. Recursive case: a call with a “smaller” problem.

c

int factorial(int n) { if (n <= 1) return 1; /* base */ return n * factorial(n - 1); /* recursive */ }

Function Pointers (Brief)

C lets you store functions in variables, enabling callbacks:

c

int (*op)(int, int) = add; int result = op(3, 4);

Full Practical Code Example with Explanation

Below are 11 problems, each a complete C program.

Problem 1: Print “Hello” Using a Function

c

#include <stdio.h> void printHello(void) { printf("Hello\n"); } int main(void) { printHello(); return 0; }

Expected output:

text

Hello

Problem 2: Function to Print Sum

Prints the sum of two integers (returns void).

c

#include <stdio.h> void printSum(int a, int b) { printf("%d\n", a + b); } int main(void) { printSum(3, 4); return 0; }

Expected output:

text

7

Problem 3: Function to Return Sum

Returns the sum.

c

#include <stdio.h> int add(int a, int b) { return a + b; } int main(void) { printf("%d\n", add(10, 20)); return 0; }

Expected output:

text

30

Problem 4: Factorial Using a Function

Returns n!.

c

#include <stdio.h> unsigned long long factorial(int n) { if (n < 0) return 0; /* error sentinel */ unsigned long long result = 1; for (int i = 2; i <= n; i++) result *= i; return result; } int main(void) { for (int i = 0; i <= 10; i++) { printf("%d! = %llu\n", i, factorial(i)); } return 0; }

Expected output:

text

0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800

Note: 20! overflows unsigned long long; use a bignum for larger inputs.

Problem 5: Prime Factorization

Print the prime factorization of n.

c

#include <stdio.h> void primeFactorize(long long n) { if (n <= 1) return; for (long long p = 2; p * p <= n; p++) { while (n % p == 0) { printf("%lld ", p); n /= p; } } if (n > 1) printf("%lld", n); printf("\n"); } int main(void) { primeFactorize(60); /* 2 2 3 5 */ primeFactorize(97); /* 97 */ primeFactorize(1); /* (nothing) */ return 0; }

Expected output:

text

2 2 3 5 97

Notes: The loop stops at p * p <= n; if a remainder >1 is left, it’s a prime factor.

Problem 6: Table Difference

Given n, print the difference between n × i and n × (i-1) for i = 1..10.

c

#include <stdio.h> void tableDiff(int n) { for (int i = 1; i <= 10; i++) { printf("%d ", n * i - n * (i - 1)); } printf("\n"); } int main(void) { tableDiff(7); return 0; }

Expected output:

text

7 7 7 7 7 7 7 7 7 7

Notes: Mathematically the difference is always n; the exercise tests loop and function basics.

Problem 7: Decimal to Binary

Convert a non-negative integer to its binary representation.

c

#include <stdio.h> void decimalToBinary(unsigned int n) { if (n == 0) { printf("0\n"); return; } char buf[33]; /* 32 bits + null */ int i = 0; while (n > 0) { buf[i++] = '0' + (n & 1u); n >>= 1; } /* Reverse */ for (int j = i - 1; j >= 0; j--) putchar(buf[j]); putchar('\n'); } int main(void) { decimalToBinary(0); /* 0 */ decimalToBinary(5); /* 101 */ decimalToBinary(255); /* 11111111 */ decimalToBinary(1024); /* 10000000000 */ return 0; }

Expected output:

text

0 101 11111111 10000000000

Problem 8: Binary to Decimal

Parse a binary string into an integer.

c

#include <stdio.h> #include <string.h> unsigned long long binaryToDecimal(const char *s) { unsigned long long value = 0; for (int i = 0; s[i] != '\0'; i++) { if (s[i] != '0' && s[i] != '1') return 0; /* invalid */ value = (value << 1) | (unsigned long long)(s[i] - '0'); } return value; } int main(void) { printf("%llu\n", binaryToDecimal("0")); /* 0 */ printf("%llu\n", binaryToDecimal("101")); /* 5 */ printf("%llu\n", binaryToDecimal("11111111")); /* 255 */ return 0; }

Expected output:

text

0 5 255

Problem 9: Digital Root

The digital root of a non-negative integer is the repeated digit-sum until a single digit remains.

c

#include <stdio.h> int digitalRoot(long long n) { if (n == 0) return 0; if (n < 0) n = -n; return 1 + (int)((n - 1) % 9); } int main(void) { printf("%d\n", digitalRoot(0)); /* 0 */ printf("%d\n", digitalRoot(9875)); /* 2 */ printf("%d\n", digitalRoot(12345)); /* 6 */ printf("%d\n", digitalRoot(9)); /* 9 */ return 0; }

Expected output:

text

0 2 6 9

Notes: The formula 1 + (n-1) % 9 is O(1); the iterative version is O(log n).

Problem 10: Sum of Digits of a Number

c

#include <stdio.h> int digitSum(long long n) { if (n < 0) n = -n; int sum = 0; while (n > 0) { sum += (int)(n % 10); n /= 10; } return sum; } int main(void) { printf("%d\n", digitSum(0)); /* 0 */ printf("%d\n", digitSum(12345)); /* 15 */ printf("%d\n", digitSum(-987)); /* 24 */ return 0; }

Expected output:

text

0 15 24

Problem 11: Fibonacci Using Recursion

c

#include <stdio.h> unsigned long long fib(unsigned int n) { if (n <= 1) return n; return fib(n - 1) + fib(n - 2); } int main(void) { for (unsigned int i = 0; i <= 12; i++) { printf("F(%u) = %llu\n", i, fib(i)); } return 0; }

Expected output:

text

F(0) = 0 F(1) = 1 F(2) = 1 F(3) = 2 F(4) = 3 F(5) = 5 F(6) = 8 F(7) = 13 F(8) = 21 F(9) = 34 F(10) = 55 F(11) = 89 F(12) = 144

Notes: Naive recursion is O(2ⁿ). For n > 40, switch to memoization or iteration.


Common Pitfalls, Edge Cases & Memory Considerations

1. Missing or Wrong Prototype

If a function is called before its definition, a prototype is mandatory. Mismatched prototypes cause link errors.

2. Returning a Pointer to a Local

c

int *bad(void) { int x = 5; return &x; } /* UB */

Return a static variable, allocate on heap, or pass a buffer.

3. Recursion Without Base Case

c

int fact(int n) { return n * fact(n - 1); } /* infinite recursion */

Always define the base case.

4. Overflowing Return Types

int factorial overflows at 13!. Use unsigned long long for n ≤ 20; beyond that, use bignums.

5. Integer Division Truncation

c

double avg = sum / n; /* integer division if both are int */ double avg = (double)sum / n; /* correct */

6. Off-by-One in Loops

for (i = 1; i <= n; i++) iterates n times; for (i = 0; i < n; i++) also n times. Pick per problem.

7. Mutable Global State in Recursion

Recursive functions should be pure. Avoid static counters unless intentional.

8. Uninitialized Pointer Parameters

If a function receives int *p, verify p != NULL before dereferencing.

9. char in <ctype.h>

Cast to unsigned char before calling isdigit, isalpha, etc.

10. Memory Considerations

  • Prefer stack-allocated buffers for small data.
  • Use malloc/free when size is unknown at compile time.
  • Never return pointers to stack-allocated data.
  • Recursion consumes stack proportional to depth — watch out for deep recursion.

Summary & Best Practices

Functions are the atom of good C code. Practice with small, complete examples teaches you the interface and the mechanics simultaneously.

Key Takeaways

  • Functions encapsulate logic and enable reuse.
  • Pass by value by default; use pointers to modify caller state.
  • Recursion needs a base case and progress toward it.
  • Function signatures are contracts — be consistent.
  • Return types limit the range of values safely returned.

Best Practices

  1. Write small, single-purpose functions.
  2. Always provide prototypes (or place definitions before use).
  3. Name functions with verbs (computeSum, printTable).
  4. Handle boundary conditions: n = 0, negatives, overflow.
  5. Use void explicitly for no-return functions and no-parameter signatures.
  6. Prefer iteration over recursion when the recursion isn’t natural.
  7. Use memoization for expensive recursive computations.
  8. Return status via value, output via pointer.
  9. Compile with -Wall -Wextra -pedantic -std=c11.
  10. Test each function in isolation before composing programs.

Suggested Practice Order

  1. Print Hello
  2. Print Sum
  3. Return Sum
  4. Factorial
  5. Prime Factorization
  6. Table Difference
  7. Decimal to Binary
  8. Binary to Decimal
  9. Digital Root
  10. Sum of Digits
  11. Fibonacci Using Recursion

Working through this list with the templates above builds the muscle memory required for larger C projects.

Leave a Reply

Your email address will not be published. Required fields are marked *