这里的代码可以分成三个文件——matrix.h、matrix.c 和 test.matrix.c。
-
标头 matrix.h 声明了一个不透明类型 Matrix 和一些操作该类型的函数 - 构造函数(我的 comment 中的第三个选项)、析构函数、获取和设置元素值的例程以及用于获取数组维度。
-
实现文件matrix.c 定义了函数。它使用断言来确保参数的有效性——懒惰但有效,只要你从不使用-DNDEBUG 编译。如果您确实禁止断言,则需要将适当的错误处理放入函数中。决定如何处理由损坏的数据结构检测到的错误是一个项目范围的决策。如果在构造矩阵期间分配失败,代码会小心释放任何已分配的内存。它定义了struct Matrix,但在此源文件之外无法访问该类型。
-
测试文件test.matrix.c 提供了一些代码来使用matrix.h 中声明的所有函数,并演示了正方形和矩形矩阵的工作原理。该文件中的代码不能取消引用矩阵结构;它必须使用访问函数。
生成的代码相当大,但它完成了工作并证明您确实可以使用结构来存储矩阵。编写一个矩阵乘法例程是一个简单的练习。
/* SOF - matrix.h */
#ifndef MATRIX_H_INCLUDED
#define MATRIX_H_INCLUDED
#include <stddef.h> /* size_t */
enum { MATRIX_MAX_SIZE = 1000 };
typedef struct Matrix Matrix; /* Opaque type */
extern Matrix *matrix_constructor(size_t rows, size_t cols);
extern void matrix_destructor(Matrix *mtx);
extern int matrix_get_element(const Matrix *mtx, size_t r, size_t c);
extern void matrix_set_element(Matrix *mtx, size_t r, size_t c, int v);
extern size_t matrix_get_cols(const Matrix *mtx);
extern size_t matrix_get_rows(const Matrix *mtx);
#endif /* MATRIX_H_INCLUDED */
/* EOF - matrix.h */
/* SOF - matrix.c */
/*#include "matrix.h"*/
#include <assert.h>
#include <stdlib.h>
struct Matrix
{
size_t rows;
size_t cols;
int **data;
};
/* It is important to prevent memory leaks on allocation failure */
Matrix *matrix_constructor(size_t rows, size_t cols)
{
assert(rows <= MATRIX_MAX_SIZE && rows > 0);
assert(cols <= MATRIX_MAX_SIZE && cols > 0);
Matrix *mtx = malloc(sizeof(*mtx));
if (mtx == NULL)
return NULL;
mtx->data = malloc(sizeof(*mtx->data) * rows);
if (mtx->data == NULL)
{
free(mtx);
return NULL;
}
for (size_t i = 0; i < rows; i++)
{
mtx->data[i] = malloc(sizeof(*mtx->data[i]) * cols);
if (mtx->data[i] == NULL)
{
while (i > 0)
free(mtx->data[--i]);
free(mtx);
return NULL;
}
}
mtx->rows = rows;
mtx->cols = cols;
return mtx;
}
void matrix_destructor(Matrix *mtx)
{
assert(mtx != NULL && mtx->data != NULL);
assert(mtx->rows <= MATRIX_MAX_SIZE && mtx->rows > 0);
assert(mtx->cols <= MATRIX_MAX_SIZE && mtx->cols > 0);
for (size_t i = 0; i < mtx->rows; i++)
free(mtx->data[i]);
free(mtx->data);
free(mtx);
}
int matrix_get_element(const Matrix *mtx, size_t r, size_t c)
{
assert(mtx != NULL && mtx->data != NULL);
assert(mtx->rows <= MATRIX_MAX_SIZE && mtx->rows > 0);
assert(mtx->cols <= MATRIX_MAX_SIZE && mtx->cols > 0);
return mtx->data[r][c];
}
void matrix_set_element(Matrix *mtx, size_t r, size_t c, int v)
{
assert(mtx != NULL && mtx->data != NULL);
assert(mtx->rows <= MATRIX_MAX_SIZE && mtx->rows > 0);
assert(mtx->cols <= MATRIX_MAX_SIZE && mtx->cols > 0);
mtx->data[r][c] = v;
}
size_t matrix_get_rows(const Matrix *mtx)
{
assert(mtx != NULL && mtx->data != NULL);
assert(mtx->rows <= MATRIX_MAX_SIZE && mtx->rows > 0);
assert(mtx->cols <= MATRIX_MAX_SIZE && mtx->cols > 0);
return mtx->rows;
}
size_t matrix_get_cols(const Matrix *mtx)
{
assert(mtx != NULL && mtx->data != NULL);
assert(mtx->rows <= MATRIX_MAX_SIZE && mtx->rows > 0);
assert(mtx->cols <= MATRIX_MAX_SIZE && mtx->cols > 0);
return mtx->cols;
}
/* EOF - matrix.c */
/* SOF - test.matrix.c */
/*#include "matrix.h"*/
#include <errno.h>
#include <stdarg.h>
#include <stdint.h> /* intmax_t */
#include <stdio.h>
/*#include <stdlib.h>*/
#include <string.h>
#include <time.h>
/* Cannot dereference Matrix pointers here! */
static void matrix_print(const char *tag, const Matrix *mtx, int width)
{
size_t rows = matrix_get_rows(mtx);
size_t cols = matrix_get_cols(mtx);
printf("%s (%zux%zu):\n", tag, rows, cols);
for (size_t r = 0; r < rows; r++)
{
const char *pad = "";
for (size_t c = 0; c < cols; c++)
{
printf("%s%*d", pad, width, matrix_get_element(mtx, r, c));
pad = ", ";
}
putchar('\n');
}
}
static void matrix_random(Matrix *mtx)
{
size_t rows = matrix_get_rows(mtx);
size_t cols = matrix_get_cols(mtx);
for (size_t r = 0; r < rows; r++)
{
for (size_t c = 0; c < cols; c++)
matrix_set_element(mtx, r, c, (rand() % (rows * cols) + 1));
}
}
static int i_log10(size_t n)
{
if (n < 10)
return 1;
int c = 1;
while (n >= 10)
{
n /= 10;
c++;
}
return c;
}
static int i_pow10(int n)
{
int r = 1;
while (n-- > 0)
r *= 10;
return r;
}
static void matrix_ordered(Matrix *mtx)
{
size_t rows = matrix_get_rows(mtx);
size_t cols = matrix_get_cols(mtx);
int mul = i_pow10(i_log10(cols));
for (size_t r = 0; r < rows; r++)
{
for (size_t c = 0; c < cols; c++)
matrix_set_element(mtx, r, c, (r + 1) * mul + c + 1);
}
}
static void err_exit(const char *fmt, ...)
{
va_list args;
int errnum = errno;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
if (errnum != 0)
fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
putc('\n', stderr);
exit(EXIT_FAILURE);
}
static Matrix *matrix_checked_constructor(const char *tag, size_t rows, size_t cols)
{
Matrix *mp = matrix_constructor(rows, cols);
if (mp == NULL)
err_exit("Failed to construct matrix %s", tag);
return mp;
}
int main(void)
{
Matrix *mi5 = matrix_checked_constructor("MI5", 5, 5);
Matrix *mi6 = matrix_checked_constructor("MI6", 6, 6);
Matrix *ks69 = matrix_checked_constructor("KS69", 6, 9);
Matrix *bw1815 = matrix_checked_constructor("BW1815", 18, 15);
time_t now = time(0);
srand(now);
printf("Seed: %jd\n", (intmax_t)now);
matrix_random(mi5);
matrix_random(mi6);
matrix_ordered(ks69);
matrix_ordered(bw1815);
matrix_print("MI5", mi5, 2);
matrix_print("MI6", mi6, 2);
matrix_print("KS69", ks69, 3);
matrix_print("BW1815", bw1815, 4);
matrix_destructor(mi5);
matrix_destructor(mi6);
return 0;
}
/* EOF - test.matrix.c */
示例输出:
Seed: 1605897737
MI5 (5x5):
14, 22, 5, 21, 11
23, 5, 23, 7, 2
10, 9, 9, 2, 24
10, 6, 21, 6, 11
5, 11, 5, 18, 3
MI6 (6x6):
25, 33, 4, 18, 24, 19
6, 1, 23, 19, 6, 24
17, 2, 26, 3, 2, 32
7, 34, 8, 5, 11, 33
6, 7, 34, 13, 21, 14
15, 25, 32, 11, 28, 28
KS69 (6x9):
11, 12, 13, 14, 15, 16, 17, 18, 19
21, 22, 23, 24, 25, 26, 27, 28, 29
31, 32, 33, 34, 35, 36, 37, 38, 39
41, 42, 43, 44, 45, 46, 47, 48, 49
51, 52, 53, 54, 55, 56, 57, 58, 59
61, 62, 63, 64, 65, 66, 67, 68, 69
BW1815 (18x15):
101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115
201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215
301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315
401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415
501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515
601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615
701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715
801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815
901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915
1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015
1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115
1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210, 1211, 1212, 1213, 1214, 1215
1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, 1309, 1310, 1311, 1312, 1313, 1314, 1315
1401, 1402, 1403, 1404, 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412, 1413, 1414, 1415
1501, 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1509, 1510, 1511, 1512, 1513, 1514, 1515
1601, 1602, 1603, 1604, 1605, 1606, 1607, 1608, 1609, 1610, 1611, 1612, 1613, 1614, 1615
1701, 1702, 1703, 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, 1712, 1713, 1714, 1715
1801, 1802, 1803, 1804, 1805, 1806, 1807, 1808, 1809, 1810, 1811, 1812, 1813, 1814, 1815