Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
Matrix Multiplication in C
Understand the row-times-column rule, write the three-loop program, and learn why this one algorithm sits behind filters, transforms and neural networks in embedded C programming.
Matrix Multiplication in C Explained
Matrix multiplication in C takes two 2D arrays and produces a third one in which each cell is a sum of products: one row of the first matrix paired with one column of the second. It is harder than addition because three loops are involved, but the pattern is the same one you will meet in digital filters, coordinate transforms and the inference code of tiny machine-learning models. This lecture of our free C programming course walks through it slowly, with a verified program and expected output, so that this free embedded C course leaves you able to write it from memory.
What You Will Learn
- When two matrices can be multiplied and what size the answer has
- The row-times-column rule, worked by hand with real numbers
- Why three nested loops are needed and what each one does
- A complete C program, how to build it, and its exact output
- Why A times B is not the same as B times A
- Cost and speed considerations for microcontrollers
Prerequisites
You should have finished the lectures on 2D array initialization and matrix addition. Comfort with nested for loops and the += operator is required.
When Can You Multiply Two Matrices?
Multiplication has a stricter condition than addition. The number of columns of the first matrix must equal the number of rows of the second. If A is M by N and B is N by P, the product C is M by P. The inner size N disappears in the result. Think of it as a small formula: (M x N) times (N x P) gives (M x P).
| Matrix A | Matrix B | Can multiply? | Result size |
|---|---|---|---|
| 2 x 3 | 3 x 2 | Yes | 2 x 2 |
| 3 x 2 | 2 x 3 | Yes | 3 x 3 |
| 2 x 3 | 2 x 3 | No (3 is not 2) | Undefined |
| 4 x 4 | 4 x 1 | Yes | 4 x 1 |
The Row-Times-Column Rule by Hand
To get the cell at row i, column j of the result, take row i of A and column j of B. Multiply them element by element, then add all those products together. This is called a dot product. We will use these matrices throughout:
Our example: A (2×3) times B (3×2)
| Matrix A | Matrix B |
|---|---|
| 1 2 3 | 7 8 |
| 4 5 6 | 9 10 |
| 11 12 |
Working out all four cells of the 2×2 answer:
Each result cell is one dot product
| Result cell | Row of A used | Column of B used | Calculation | Value |
|---|---|---|---|---|
| C[0][0] | 1 2 3 | 7 9 11 | 1*7 + 2*9 + 3*11 | 58 |
| C[0][1] | 1 2 3 | 8 10 12 | 1*8 + 2*10 + 3*12 | 64 |
| C[1][0] | 4 5 6 | 7 9 11 | 4*7 + 5*9 + 6*11 | 139 |
| C[1][1] | 4 5 6 | 8 10 12 | 4*8 + 5*10 + 6*12 | 154 |
Keep this table beside you while reading the code, because every line of the program is one piece of it.
What Each of the Three Loops Does
Three nested loops and their jobs
The outer two loops select which result cell we are building, exactly like in matrix addition. The new innermost loop, k, runs along the shared dimension N and accumulates the products. In the body, a[i][k] moves across a row while b[k][j] moves down a column.
Program: Matrix Multiplication in C
/* matmul.c - multiply a 2x3 matrix by a 3x2 matrix */
#include <stdio.h>
#define M 2 /* rows of A */
#define N 3 /* cols of A = rows of B */
#define P 2 /* cols of B */
int main(void)
{
int a[M][N] = { {1, 2, 3}, {4, 5, 6} };
int b[N][P] = { {7, 8}, {9, 10}, {11, 12} };
int c[M][P] = {0};
for (int i = 0; i < M; i++)
for (int j = 0; j < P; j++)
for (int k = 0; k < N; k++)
c[i][j] += a[i][k] * b[k][j];
printf("C (%dx%d):\n", M, P);
for (int i = 0; i < M; i++) {
for (int j = 0; j < P; j++)
printf("%5d", c[i][j]);
printf("\n");
}
return 0;
}
$ gcc -std=c11 -Wall -Wextra -o matmul matmul.c
$ ./matmul
C (2x2):
58 64
139 154
The output matches our hand calculation. Notice three design choices. The shared size N is a single macro used for both the column count of A and the row count of B, so the compatibility rule is built into the declarations: you cannot accidentally give them different sizes. The result array is declared with = {0} so every accumulator starts at zero. And we use += so each pass of the k loop adds one product.
Order Matters: A x B Is Not B x A
With ordinary numbers, 3 times 4 equals 4 times 3. Matrices do not behave like that. Swap our two matrices and the product B x A is a 3×3 matrix, not even the same shape as A x B:
B (3×2) times A (2×3) gives a 3×3 result
| Row | col 0 | col 1 | col 2 |
|---|---|---|---|
| row 0 | 39 | 54 | 69 |
| row 1 | 49 | 68 | 87 |
| row 2 | 59 | 82 | 105 |
You can check it by swapping the roles in the program: declare a as 3×2, b as 2×3 and c as 3×3, and keep the same loops with sizes (3, 2, 3). Even when both orders are possible, the answers differ, which is why in firmware that applies a rotation and then a scale, the order of operations is part of the design.
Matrix Multiplication in Embedded C Programming
You will almost never see a textbook “multiply two big matrices” job on an MCU, but the same loop appears in disguise everywhere:
- Digital filters: an FIR filter output is a dot product of coefficients and recent samples.
- Coordinate transforms: converting accelerometer axes into body axes uses a 3×3 matrix times a 3×1 vector.
- Color conversion: RGB to YUV is a 3×3 matrix applied to every pixel.
- Tiny neural networks: each dense layer is a matrix-vector product.
A matrix times a vector is simply the case P = 1. Your same three loops work, with the j loop running only once.
Cost of Multiplication on a Microcontroller
The number of multiply-add operations is M times N times P. For our 2×3 by 3×2 example that is 12. For two square matrices of side n it is n cubed, so going from 8×8 to 16×16 makes the job eight times larger. On a chip without a hardware floating-point unit, multiplying floats is slow because it runs in software, so embedded C programming often uses integer or fixed-point values instead. Always think about the accumulator size as well: the sum of many products can exceed the range of the input type, so use a wider type such as a 32-bit int for 16-bit data.
| Concern | Why it matters | Typical approach |
|---|---|---|
| Loop count | Grows as M x N x P | Keep matrices small, unroll tiny fixed sizes |
| No FPU | Software float is slow | Use integers or fixed-point |
| Accumulator overflow | Sums outgrow the element type | Accumulate in a wider integer |
| Memory access order | b[k][j] jumps a whole row per step | Fine for small sizes; use a library or DSP unit for large ones |
Common Mistakes and Troubleshooting
| Mistake | Symptom | Fix |
|---|---|---|
| Using a[i][j] * b[i][j] | That is element-wise multiplication, not matrix multiplication | Use a[i][k] * b[k][j] with a k loop |
| Result array not zeroed | Huge or random numbers | Initialize with {0} or clear before the k loop |
| Wrong loop limits | Out-of-bounds read, strange values | i to M, j to P, k to N |
| Result declared as M x N | Overwrites memory or loses columns | Result is M x P |
| Assuming A x B equals B x A | Wrong design in a transform chain | Check order and sizes every time |
Best Practices
- Name the three sizes M, N and P (or clearer names) and use them everywhere.
- Test with a small case you can verify by hand before trusting larger data.
- Test the identity: multiplying any matrix by an identity matrix of the right size must return the original.
- Accumulate in a local variable inside the k loop if you want the compiler to keep it in a register.
- Document the expected shapes in a comment above any function that multiplies matrices.
- For large sizes, prefer a vendor DSP library over a hand-rolled triple loop.
Summary and Key Takeaways
- Columns of A must equal rows of B; the result is rows of A by columns of B.
- Each result cell is a dot product: sum of a[i][k] * b[k][j] over k.
- Three nested loops: rows, columns, and the shared dimension.
- The result array must start at zero because it accumulates.
- Matrix multiplication is not commutative.
- The work grows as M x N x P, so size and data type matter on small devices.
Conclusion
Matrix multiplication looks intimidating until you split it into the dot product it really is. Once you can write the three loops from memory and explain the sizes, you have covered one of the most reused patterns in embedded C programming. Next in this free c tutorial we flip a matrix on its side with the transpose.
Frequently Asked Questions
What is the condition for matrix multiplication in C?
The number of columns of the first matrix must equal the number of rows of the second matrix.
What size is the result of multiplying an M x N matrix by an N x P matrix?
The result is M x P. The shared dimension N is consumed by the dot products.
Why do we need three loops?
Two loops choose the result cell, and the third loop sums the products along the shared dimension for that cell.
Why must the result array be initialized to zero?
Each cell accumulates a sum with +=. Starting from a garbage value would corrupt every result.
Is a[i][j] * b[i][j] matrix multiplication?
No, that is element-wise (Hadamard) multiplication. True matrix multiplication uses a[i][k] * b[k][j].
Is matrix multiplication commutative?
No. A x B and B x A usually differ, and they may not even have the same size.
How can I make matrix multiplication faster on a microcontroller?
Use integer or fixed-point math, keep matrices small, accumulate in registers and use the chip’s DSP or SIMD instructions through a vendor library.
Master Embedded C Programming for Free
Next: the transpose of a matrix. Continue the free embedded C course on EmbeddedPathashala and build skills that carry straight into firmware work.
Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.

2 Comments