The question is related to more general ones that have been answered there:
How are multi-dimensional arrays formatted in memory? / How to pass a 2D array by pointer in C?
Here, an attempt to create simple matrix comparison functions, that illustrate how plain arrays can be used directly:
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
/* VARIANT 0: compare elements of arrays in a nested loop
-> no obvious advantages over VARIANT 1 */
bool matrix_equal0(
const int *a, const int *b, unsigned int rows, unsigned int columns)
{
for (unsigned int i = 0; i < rows; i++) {
for (unsigned int j = 0; j < columns; j++) {
if (a[i * columns + j] != b[i * columns + j]) {
return false;
}
}
}
return true;
}
/* VARIANT 1: compare elements of matrices one by one,
simpler and more performant than VARIANT 0 */
bool matrix_equal1(
const int *a, const int *b, unsigned int rows, unsigned int columns)
{
unsigned int i = rows * columns;
while (i--) {
if (*a++ != *b++) {
return false;
}
}
return true;
}
/* VARIANT 2:
compare both arrays as a chunk of memory (possible but not recommended) */
bool matrix_equal2(
const int *a, const int *b, unsigned int rows, unsigned int columns)
{
return (memcmp(a, b, rows * columns * sizeof(int)) == 0);
}
int main(void)
{
const int mat0[3][3] = {
{ 1, 2, 3, },
{ 4, 5, 6, },
{ 7, 8, 9, },
};
const int mat1[3][3] = {
{ 1, 2, 3, },
{ 4, 5, 6, },
{ 7, 8, 9, },
};
const int mat2[3][3] = {
{ 1, 2, 3, },
{ 4, 5, 6, },
{ 7, 8, 9999, },
};
if (matrix_equal0(&mat0[0][0], &mat1[0][0], 3, 3)) {
printf("VARIANT 0: mat0 and mat1 are equal!\n");
}
if (matrix_equal0(&mat0[0][0], &mat2[0][0], 3, 3) == false) {
printf("VARIANT 0: mat0 and mat2 are NOT equal!\n");
}
if (matrix_equal1(&mat0[0][0], &mat1[0][0], 3, 3)) {
printf("VARIANT 1: mat0 and mat1 are equal!\n");
}
if (matrix_equal1(&mat0[0][0], &mat2[0][0], 3, 3) == false) {
printf("VARIANT 1: mat0 and mat2 are NOT equal!\n");
}
if (matrix_equal2(&mat0[0][0], &mat1[0][0], 3, 3)) {
printf("VARIANT 2: mat0 and mat1 are equal!\n");
}
if (matrix_equal2(&mat0[0][0], &mat2[0][0], 3, 3) == false) {
printf("VARIANT 2: mat0 and mat2 are NOT equal!\n");
}
return 0;
}
Output:
VARIANT 0: mat0 and mat1 are equal!
VARIANT 0: mat0 and mat2 are NOT equal!
VARIANT 1: mat0 and mat1 are equal!
VARIANT 1: mat0 and mat2 are NOT equal!
VARIANT 2: mat0 and mat1 are equal!
VARIANT 2: mat0 and mat2 are NOT equal!
NOTE: Caution should be taken when 2D matrices are treated as plain arrays. How the columns and rows relate to the 1D memory layout of the array can be a source of confusion and cause bugs. - Here a quick example, that illustrates the relationship:
#include <stdio.h>
/* print the elements of a 2D matrix */
void matrix_print(const int *mat, unsigned int rows, unsigned int columns)
{
for (unsigned int i = 0; i < rows; i++) {
for (unsigned int j = 0; j < columns; j++) {
printf("%d ", mat[i * columns + j]);
}
printf("\n");
}
printf("\n");
}
int main(void)
{
const int mat[2][3] = {
{ 1, 2, 3, },
{ 4, 5, 6, },
};
matrix_print(&mat[0][0], 2, 3);
return 0;
}
Output:
1 2 3
4 5 6
If your project requires lots of matrix math, it might make sense to use a custom type to make them easier to handle and to build up a library-esque set of utility functions. - Here an example that illustrates how a compare function might look like in that case:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
/* VARIANT 3: instead of using a 2D array to represent a matrix,
a custom type is used. That makes it easier/nicer to pass
matrices around various places in your code. */
typedef struct {
unsigned int rows;
unsigned int columns;
int *elements;
} matrix;
matrix matrix_new(unsigned int rows, unsigned int columns)
{
matrix mat;
mat.rows = rows;
mat.columns = columns;
mat.elements = calloc(rows * columns, sizeof(int)); /* zero matrix */
return mat;
}
bool matrix_clear(matrix *mat)
{
if (mat == NULL)
return false;
mat->rows = 0;
mat->columns = 0;
free(mat->elements);
mat->elements = NULL;
return true;
}
bool matrix_elem(matrix *mat, unsigned int row, unsigned int column, int val)
{
if (mat == NULL)
return false;
if (row >= mat->rows || column >= mat->columns)
return false;
if (mat->elements == NULL)
return false;
mat->elements[row * mat->columns + column] = val;
return true;
}
bool matrix_compare(const matrix *mat0, const matrix *mat1)
{
if (mat0 == NULL || mat1 == NULL)
return false;
if (mat0->rows != mat1->rows)
return false;
if (mat0->columns != mat1->columns)
return false;
int *a = mat0->elements;
int *b = mat1->elements;
unsigned int i = mat0->rows * mat0->columns;
while (i--) {
if (*a++ != *b++) {
return false;
}
}
return true;
}
int main(void)
{
matrix mat3 = matrix_new(3, 3);
matrix mat4 = matrix_new(3, 3);
matrix mat5 = matrix_new(3, 3);
matrix_elem(&mat3, 1, 1, 12);
matrix_elem(&mat4, 1, 1, 12);
matrix_elem(&mat5, 1, 1, 13);
//matrix_copy_from_2d_array(&mat6, mat0); // todo
if (matrix_compare(&mat3, &mat4)) {
printf("VARIANT 3: mat3 and mat4 are equal!\n");
}
if (matrix_compare(&mat3, &mat5) == false) {
printf("VARIANT 3: mat3 and mat5 are NOT equal!\n");
}
matrix_clear(&mat3);
matrix_clear(&mat4);
matrix_clear(&mat5);
return 0;
}
Output:
VARIANT 3: mat3 and mat4 are equal!
VARIANT 3: mat3 and mat5 are NOT equal!