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
| Concept | Purpose |
|---|---|
| Function prototype | Declares the signature to the compiler |
| Function definition | Provides the implementation |
| Parameters | Input values (copied by default) |
| Return value | Single output via return |
| Recursion | A function calling itself |
void | Used 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
- Base case: when the function stops calling itself.
- 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/freewhen 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
- Write small, single-purpose functions.
- Always provide prototypes (or place definitions before use).
- Name functions with verbs (
computeSum,printTable). - Handle boundary conditions:
n = 0, negatives, overflow. - Use
voidexplicitly for no-return functions and no-parameter signatures. - Prefer iteration over recursion when the recursion isn’t natural.
- Use memoization for expensive recursive computations.
- Return status via value, output via pointer.
- Compile with
-Wall -Wextra -pedantic -std=c11. - Test each function in isolation before composing programs.
Suggested Practice Order
- Print Hello
- Print Sum
- Return Sum
- Factorial
- Prime Factorization
- Table Difference
- Decimal to Binary
- Binary to Decimal
- Digital Root
- Sum of Digits
- Fibonacci Using Recursion
Working through this list with the templates above builds the muscle memory required for larger C projects.





