Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
C Array Practice Problems
Five hands-on problems: sizeof for multidimensional arrays, swapping values versus arrays, transposed printing, the sieve of Eratosthenes and stable partitioning.
These C array practice problems turn the theory from earlier lectures into working code. Every program in this free C tutorial is original, compiled with warnings enabled and shown with real output, so you can type it, run it and modify it. Each problem also carries an embedded C programming lesson, such as why a lookup table lives in flash or why a temporary buffer costs stack space on a microcontroller. Treat this lecture as a lab session of your free embedded C course.
Topics Covered
What You Will Learn
- How
sizeofreports bytes for one-, two- and three-dimensional arrays, and how to derive row and column counts from it. - Why a function that swaps two plain integers fails while a function that swaps two arrays works.
- How to print a matrix in transposed order without creating a second matrix.
- How to generate prime numbers with the sieve of Eratosthenes using only integer arithmetic.
- How to move all negative numbers to the end while keeping the original order of both groups.
Prerequisites
- Lectures on one- and two-dimensional arrays from this chapter.
- Functions with parameters, and the difference between a value and an address.
- Nested loops and simple conditions.
How to Use These C Array Practice Problems
Do not just read the solutions. For each of the C array practice problems, first write the program yourself, then compare it with the version here. If your output differs, find out why before moving on, because the difference usually points to a gap in your understanding of indexing, scope or argument passing. A good routine is to solve a problem, change one detail such as the array size or the data, predict the new output on paper and only then run it. This habit builds the kind of debugging intuition that employers look for when they test embedded C programming skills.
Problem 1: sizeof on Multidimensional Arrays
The size of an array is the number of elements multiplied by the size of one element. For a two-dimensional array the element count is rows × columns, and for a three-dimensional array it is the product of all three dimensions. On most compilers an int is 4 bytes, but you should never hard-code that number. Print sizeof(int) as well so the output explains itself.
| Declaration | Elements | Bytes |
|---|---|---|
int line[8] | 8 | 32 |
int table[2][5] | 10 | 40 |
int cube[3][2][4] | 24 | 96 |
To recover dimensions from the size, divide: rows equal sizeof table / sizeof table[0] and columns equal sizeof table[0] / sizeof table[0][0]. This works only in the scope where the array was declared, because inside a function the array has already decayed to a pointer.
Problem 2: Swapping Values and Swapping Arrays
C passes ordinary arguments by value. A function that receives two integers gets copies, so swapping them changes nothing in the caller. Arrays behave differently because the function receives the address of the first element, so writing through the parameter changes the caller’s data. Run the program at the end of this section to see both cases side by side.
main() with no parameters. Since C99 a missing return type is an error in strict compilers, so always write void or the real type and use int main(void).Problem 3: Print a Matrix Transposed
You do not need a second array to show the transpose. Swap the roles of the loop variables: let the outer loop walk the columns and the inner loop walk the rows, but still index as m[row][col]. A 2 × 3 matrix is then printed as 3 rows of 2 numbers.
Problem 4: Sieve of Eratosthenes in C
The sieve finds all primes below a limit. Mark every number from 2 upwards as prime, then for each prime p cross out its multiples. You can start crossing at p × p because smaller multiples were already crossed by smaller primes, and you can stop the outer loop when p × p reaches the limit. This avoids sqrt, floating point and the maths library, which matters on microcontrollers without a floating-point unit.
Problem 5: Move Negatives to the End, Keep Order
This is called a stable partition. The simplest correct approach uses a scratch buffer: copy the non-negative values first, then the negative values, then copy everything back. The order inside each group is preserved because each pass reads left to right. An in-place alternative shifts elements one by one, which saves memory but costs more time.
| Approach | Time | Extra memory | Best for |
|---|---|---|---|
| Scratch buffer, two passes | O(n) | O(n) | Small arrays, clear code |
| In-place shifting | O(n²) | O(1) | Tight RAM budgets |
Complete Program for All Five Problems
#include <stdio.h>
#define LIMIT 60
#define NUM_COUNT 10
static void swap_values(int a, int b)
{
int tmp = a;
a = b;
b = tmp;
}
static void swap_arrays(int first[], int second[], int len)
{
for (int i = 0; i < len; i++) {
int tmp = first[i];
first[i] = second[i];
second[i] = tmp;
}
}
static void print_ints(const char *label, const int a[], int len)
{
printf("%s", label);
for (int i = 0; i < len; i++)
printf(" %d", a[i]);
putchar('\n');
}
static void show_sizes(void)
{
int line[8];
int table[2][5];
int cube[3][2][4];
printf("sizeof(int) = %zu\n", sizeof(int));
printf("line[8] = %zu bytes\n", sizeof line);
printf("table[2][5] = %zu bytes (%zu rows, %zu cols)\n", sizeof table,
sizeof table / sizeof table[0], sizeof table[0] / sizeof table[0][0]);
printf("cube[3][2][4] = %zu bytes\n", sizeof cube);
}
static void print_transposed(void)
{
int m[2][3] = {{1, 2, 3}, {4, 5, 6}};
for (int col = 0; col < 3; col++) {
for (int row = 0; row < 2; row++)
printf("%3d", m[row][col]);
putchar('\n');
}
}
static void sieve(void)
{
unsigned char is_prime[LIMIT];
for (int i = 0; i < LIMIT; i++)
is_prime[i] = (i >= 2);
for (int p = 2; p * p < LIMIT; p++) {
if (!is_prime[p])
continue;
for (int multiple = p * p; multiple < LIMIT; multiple += p)
is_prime[multiple] = 0;
}
printf("primes below %d:", LIMIT);
for (int i = 2; i < LIMIT; i++)
if (is_prime[i])
printf(" %d", i);
putchar('\n');
}
static void negatives_last(int a[], int len)
{
int scratch[NUM_COUNT];
int w = 0;
if (len > NUM_COUNT)
return;
for (int i = 0; i < len; i++)
if (a[i] >= 0)
scratch[w++] = a[i];
for (int i = 0; i < len; i++)
if (a[i] < 0)
scratch[w++] = a[i];
for (int i = 0; i < len; i++)
a[i] = scratch[i];
}
int main(void)
{
int x = 4, y = 6;
int p[4] = {1, 2, 3, 4};
int q[4] = {9, 8, 7, 6};
int mixed[NUM_COUNT] = {7, -2, 5, -9, 4, -1, 8, 3, -6, 0};
puts("--- Problem 1: sizeof ---");
show_sizes();
puts("--- Problem 2: swapping ---");
swap_values(x, y);
swap_arrays(p, q, 4);
printf("after swap_values: x = %d, y = %d\n", x, y);
print_ints("after swap_arrays p:", p, 4);
print_ints("after swap_arrays q:", q, 4);
puts("--- Problem 3: transposed print ---");
print_transposed();
puts("--- Problem 4: sieve ---");
sieve();
puts("--- Problem 5: stable partition ---");
print_ints("before:", mixed, NUM_COUNT);
negatives_last(mixed, NUM_COUNT);
print_ints("after: ", mixed, NUM_COUNT);
return 0;
}
gcc -std=c11 -Wall -Wextra -o array_practice array_practice.c
./array_practice
Output:
--- Problem 1: sizeof ---
sizeof(int) = 4
line[8] = 32 bytes
table[2][5] = 40 bytes (2 rows, 5 cols)
cube[3][2][4] = 96 bytes
--- Problem 2: swapping ---
after swap_values: x = 4, y = 6
after swap_arrays p: 9 8 7 6
after swap_arrays q: 1 2 3 4
--- Problem 3: transposed print ---
1 4
2 5
3 6
--- Problem 4: sieve ---
primes below 60: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59
--- Problem 5: stable partition ---
before: 7 -2 5 -9 4 -1 8 3 -6 0
after: 7 5 4 8 3 0 -2 -9 -1 -6
Reading the Output
The sizes depend on your platform, so on a compiler with a 2-byte int every byte count halves. The values of x and y stay 4 and 6 because the function swapped only its private copies, while p and q have genuinely traded contents. The transposed print shows the columns of the original matrix as rows. The sieve prints exactly the primes below 60, and the partition keeps 7, 5, 4, 8, 3, 0 in their original relative order, followed by -2, -9, -1, -6 in their original relative order.
Real-World Embedded C Uses
- sizeof for buffers: computing element counts at compile time keeps DMA buffers and lookup tables correct when you resize them.
- Swapping buffers: double buffering swaps which buffer the hardware reads and which one the CPU writes, usually by swapping an index rather than copying data.
- Sieve-style tables: a bit or byte table marking which pins, channels or IDs are valid is a common firmware pattern.
- Stable partition: moving error or invalid samples to the end of a log while preserving order helps reporting.
Common Mistakes and Troubleshooting
| Mistake | Consequence | Fix |
|---|---|---|
| Expecting swap_values to change the caller | Values stay the same | Swap array elements, or learn pointers in the next chapters |
| Using sizeof on an array parameter | Gets pointer size | Pass the length explicitly |
| Starting the sieve inner loop at 2p | Works but does extra work | Start at p times p |
| Not checking the buffer size in negatives_last | Stack overflow of scratch array | Guard with a length check as shown |
| Hard-coding 4 for sizeof(int) | Wrong on 16-bit targets | Always use sizeof |
Best Practices, Performance and Security
- The sieve runs in roughly O(n log log n) time and uses one byte per number, so choose
unsigned charor a bit array to save RAM. - A scratch buffer on the stack must be sized with a constant. Check the incoming length before using it, otherwise a caller can overflow the buffer.
- Use
conston read-only array parameters such as inprint_ints. - Prefer
size_tfor sizes and counts when the value can never be negative.
Summary
- Array size equals element count times element size, and works only where the array is declared.
- Scalars are passed by value, arrays by address, which is why one swap fails and the other works.
- Swap loop roles to print a transpose without extra memory.
- Start the sieve at p times p and stop when p times p reaches the limit.
- A scratch buffer gives a simple, stable partition at the cost of extra RAM.
These C array practice problems cover the core patterns that later chapters on pointers and strings rely on, and solving them again from memory a week later is the best way to make them permanent. If you can explain each of the C array practice problems to a friend without looking at the code, you have understood it. Strong fundamentals here also make every other free embedded C course topic easier. Next you will solve classic array programs such as finding extremes, reversing, sorting and rotating.
FAQ: C Array Practice Problems
How do I find the number of rows and columns of a 2-D array?
Divide the total size by the size of one row for rows, and the size of one row by the size of one element for columns. Do this in the scope that declares the array.
Why does my swap function not change the variables?
C passes plain variables by value, so the function swaps copies. Arrays are passed by address, so their elements are really exchanged.
Why start the sieve inner loop at p times p?
Smaller multiples of p have a smaller prime factor and were already removed by earlier passes.
What is a stable partition?
It splits data into two groups while keeping the original order inside each group, such as non-negatives first and negatives last.
Is it safe to assume int is 4 bytes?
No. It depends on the compiler and target. Use sizeof so your code stays correct on 16-bit, 32-bit and 64-bit systems.
Keep Going With the Free C Programming Course
Every lecture in this free C tutorial for embedded systems is original, tested and written for beginners who want real embedded C programming skills. Move on to the next lecture or return to the course index.
Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.

2 Comments