数组与矩阵运算:矩阵加法、乘法,转置矩阵的 C 语言实现

矩阵运算,说白了就是批量处理数据的一种方式。我在做图像处理项目时,经常要跟矩阵打交道——比如把一张图片的像素值做转置,或者把两个滤镜矩阵做乘法。你想想看,如果没有数组,这些操作得写多少行代码?

今天我们就来聊聊矩阵的三种基本运算:加法、乘法、转置。我会用 C 语言把它们一一实现,顺便分享一些我踩过的坑。

1. 矩阵的存储方式

在 C 语言里,矩阵通常用二维数组表示。我个人习惯用 int matrix[ROW][COL] 这种静态方式,但如果矩阵大小不确定,也可以用动态内存分配。

举个例子,一个 3×3 的矩阵:

#define ROW 3
#define COL 3

int A[ROW][COL] = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

嗯,这里要注意:数组下标从 0 开始,别搞混了。我曾经在项目里因为下标越界,查了半天 bug,最后发现是 for 循环多跑了一次。

2. 矩阵加法

矩阵加法很简单——对应位置元素相加。前提是两个矩阵的行数和列数必须相同。

来看代码:

void matrix_add(int A[ROW][COL], int B[ROW][COL], int C[ROW][COL]) {
    for (int i = 0; i < ROW; i++) {
        for (int j = 0; j < COL; j++) {
            C[i][j] = A[i][j] + B[i][j];
        }
    }
}

为什么用两层循环?因为矩阵是二维的,外层循环控制行,内层循环控制列。说白了,就是挨个儿把每个位置的值加起来。

小提示: 如果矩阵很大,可以考虑用指针运算代替下标访问,能稍微提升性能。不过对于初学者,先保证正确性更重要。

3. 矩阵乘法

矩阵乘法比加法复杂一些。假设 A 是 m×n 矩阵,B 是 n×p 矩阵,结果 C 是 m×p 矩阵。C 中第 i 行第 j 列的元素,等于 A 的第 i 行与 B 的第 j 列对应元素乘积之和。

公式长这样:

C[i][j] = Σ (A[i][k] * B[k][j])   // k 从 0 到 n-1

代码实现:

void matrix_multiply(int A[][3], int B[][3], int C[][3], int m, int n, int p) {
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < p; j++) {
            C[i][j] = 0;
            for (int k = 0; k < n; k++) {
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }
}

这里有三层循环,你想想看:外层两个循环确定结果矩阵的每个位置,内层循环做累加。我在项目中遇到过一个问题——忘记把 C[i][j] 初始化为 0,结果算出来的值莫名其妙。嗯,从那以后我每次都会先清零。

注意: 矩阵乘法不满足交换律!A×B 和 B×A 通常不一样。写代码前一定要确认好维度匹配。

4. 转置矩阵

转置就是把矩阵的行和列互换。A 的转置记作 AT,如果 A 是 m×n,那么 AT 就是 n×m。

实现起来也不难:

void matrix_transpose(int A[ROW][COL], int T[COL][ROW]) {
    for (int i = 0; i < ROW; i++) {
        for (int j = 0; j < COL; j++) {
            T[j][i] = A[i][j];
        }
    }
}

注意看:赋值时下标是交换的。我刚开始学的时候老写反,后来记住一句话——「转置就是 T[j][i] = A[i][j]」,再也没错过。

5. 完整示例

下面我把三个运算整合到一个程序里,方便你对照:

#include <stdio.h>
#define ROW 3
#define COL 3

void matrix_add(int A[ROW][COL], int B[ROW][COL], int C[ROW][COL]) {
    for (int i = 0; i < ROW; i++)
        for (int j = 0; j < COL; j++)
            C[i][j] = A[i][j] + B[i][j];
}

void matrix_multiply(int A[ROW][COL], int B[ROW][COL], int C[ROW][COL]) {
    for (int i = 0; i < ROW; i++) {
        for (int j = 0; j < COL; j++) {
            C[i][j] = 0;
            for (int k = 0; k < COL; k++) {
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }
}

void matrix_transpose(int A[ROW][COL], int T[COL][ROW]) {
    for (int i = 0; i < ROW; i++)
        for (int j = 0; j < COL; j++)
            T[j][i] = A[i][j];
}

void print_matrix(int M[ROW][COL]) {
    for (int i = 0; i < ROW; i++) {
        for (int j = 0; j < COL; j++) {
            printf("%4d ", M[i][j]);
        }
        printf("\n");
    }
}

int main() {
    int A[ROW][COL] = {{1,2,3},{4,5,6},{7,8,9}};
    int B[ROW][COL] = {{9,8,7},{6,5,4},{3,2,1}};
    int C[ROW][COL];

    printf("矩阵 A:\n"); print_matrix(A);
    printf("矩阵 B:\n"); print_matrix(B);

    matrix_add(A, B, C);
    printf("A + B:\n"); print_matrix(C);

    matrix_multiply(A, B, C);
    printf("A * B:\n"); print_matrix(C);

    int T[COL][ROW];
    matrix_transpose(A, T);
    printf("A 的转置:\n");
    for (int i = 0; i < COL; i++) {
        for (int j = 0; j < ROW; j++) {
            printf("%4d ", T[i][j]);
        }
        printf("\n");
    }

    return 0;
}

6. 知识体系图

下面这张图帮你理清矩阵运算的核心逻辑:

矩阵运算核心知识体系 矩阵运算 矩阵加法 矩阵乘法 转置矩阵 对应元素相加 行列数相同 三层循环 维度匹配 行列互换 下标交换 核心:二维数组 + 循环控制

7. 避坑指南

  • 下标越界: 我曾经在循环里用了 i <= ROW,结果数组越界,程序直接崩溃。记住,数组下标从 0 开始,最大到 ROW-1
  • 乘法累加器未清零: 如果不把 C[i][j] 初始化为 0,结果会是垃圾值。我吃过这个亏,现在每次写乘法都会先清零。
  • 转置时搞反下标: 记住口诀——「转置就是 T[j][i] = A[i][j]」,写代码时多看一眼。
  • 动态内存泄漏: 如果用 malloc 分配矩阵,记得用 free 释放。我见过有人只分配不释放,内存越用越多。
核心要点: 矩阵运算的本质就是操作二维数组。加法靠两层循环,乘法靠三层循环,转置靠交换下标。把这些基础打牢,后面学图像处理、机器学习都会轻松很多。

好了,今天就聊到这里。矩阵运算看起来简单,但写对并不容易。建议你亲手敲一遍代码,跑一跑,看看结果对不对。遇到问题别慌,回头看看这篇笔记,应该能帮你解决大部分坑。

公众号:蓝海资料掘金营,微信deep3321