代码之家  ›  专栏  ›  技术社区  ›  andrey-dru-mel

malloc:损坏的顶部大小无法解决问题

  •  1
  • andrey-dru-mel  · 技术社区  · 2 年前

    我想写一个简单的C程序,接收矩阵(首先是它的大小)并反转它,所以我写了一些代码,编译它,运行它,对于大小大于2的矩阵,得到一个“malloc:损坏的顶部大小”错误。我检查了代码中未被释放的内存,但我就是看不出哪里出了问题。

    代码本身:

    #include <ctype.h>
    #include <math.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    double det(double **matrix, int n, int m);
    int invert(double **matrix, int n, int m);
    
    int input(double ***matrix, int *n, int *m);
    void output(double **matrix, int n, int m);
    
    double **allocate_matrix(int n, int m);
    
    int main(void) {
        double **matrix;
        int n, m;
        if (input(&matrix, &n, &m) == 0 && invert(matrix, n, m) == 0) {
            output(matrix, n, m);
            free(matrix);
        } else
            printf("n/a");
        return 0;
    }
    
    // Secondary functions
    
    void swap(void *a, void *b, size_t size) {
        void *c = malloc(size);
        memcpy(c, a, size);
        memcpy(a, b, size);
        memcpy(b, c, size);
        free(c);
    }
    
    // Matrix allocation
    
    double **allocate_matrix(int n, int m) {
        double **matrix = malloc(n * m * sizeof(double) + n * sizeof(double *));
        double *data = (double *)(matrix + n);
        for (int i = 0; i < n; i++) matrix[i] = data + m * i;
        for (int i = 0; i < n * m; i++) data[i] = 0;
        return matrix;
    }
    
    // Matrix operations
    
    void matrix_for_det(double **matrix, int n, int m, double **res, int y, int x) {
        int in = 0, jn = 0;
        for (int i = 0; i < n; i++) {
            if (i == y) continue;
            for (int j = 0; j < m; j++) {
                if (j == x) continue;
                res[in][jn] = matrix[i][j];
                jn++;
            }
            in++;
        }
    }
    
    void matrix_const_mul(double **matrix, int n, int m, double c) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                matrix[i][j] *= c;
            }
        }
    }
    
    double det(double **matrix, int n, int m) {
        if (n != m) return NAN;
        if (n == 2) return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix [1][0];
        if(n == 1) return matrix[0][0];
        double result = 0;
        double **next = allocate_matrix(n - 1, m - 1);
        if (next != NULL) {
            for (int col = 0; col < m; col++) {
                matrix_for_det(matrix, n, m, next, 0, col);
                double atom = matrix[0][col] * det(next, n - 1, m - 1);
                if (col % 2 == 0)
                    result += atom;
                else
                    result -= atom;
            }
            free(next);
        } else
            result = NAN;
        return result;
    }
    
    int transpose(double ***matrix, int *n, int *m) {
        int exit_status = 0;
        double **result = allocate_matrix(*m, *n);
        if (result != NULL) {
            for (int i = 0; i < *n; i++) {
                for (int j = 0; j < *m; j++) {
                    result[j][i] = (*matrix)[i][j];
                }
            }
            free(*matrix);
            *matrix = result;
            swap(n, m, sizeof(int));
        } else
            exit_status = -1;
        return exit_status;
    }
    
    int invert(double **matrix, int n, int m) {
        int exit_status = 0;
        double **new = allocate_matrix(n, m);
        double **next = allocate_matrix(n - 1, m - 1);
        if (new != NULL && next != NULL) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    matrix_for_det(matrix, n, m, next, i, j);
                    new[i][j] = det(next, n - 1, m - 1);
                    if ((i + j) % 2 == 1) new[i][j] *= -1;
                }
            }
            free(next);
    
            if (transpose(&new, &n, &m) == 0) {
                double det_v = det(matrix, n, m);
                matrix_const_mul(new, n, m, 1. / det_v);
    
                memcpy(matrix[0], new[0], n * m * sizeof(double));
            } else
                exit_status = -1;
    
            free(new);
        } else {
            if (new) free(new);
            if (next) free(next);
        }
        return exit_status;
    }
    
    // Input Output
    
    int check_string() {
        int exit_status = 0;
        int c;
        while ((c = getchar()) != EOF && c != '\n' && exit_status == 0) {
            if (!isspace(c)) exit_status = -1;
        }
        return exit_status;
    }
    
    int input(double ***matrix, int *n, int *m) {
        int exit_status = 0;
        if (scanf("%d %d", n, m) == 2 && *n > 0 && *n == *m) {
            *matrix = allocate_matrix(*n, *m);
            for (int i = 0; i < *n && exit_status == 0; i++) {
                for (int j = 0; j < *m && exit_status == 0; j++) {
                    if (scanf("%lf", (*matrix)[i] + j) != 1) exit_status = -1;
                }
            }
            exit_status += check_string();
            if (exit_status != 0) free(*matrix);
        } else
            exit_status = -1;
        return exit_status;
    }
    
    void output(double **matrix, int n, int m) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m - 1; j++) {
                printf("%.6lf ", matrix[i][j]);
            }
            if (m > 0) printf("%.6lf", matrix[i][m - 1]);
            if (i < n - 1) puts("");
        }
    }
    

    我试着使用调试来查看哪里出了问题,但它在 allocate_matrix() 从调用的函数 transpose() 在里面 result 初始化malloc中止。我不明白为什么它不像内部错误那样返回NULL,而是中止了所有程序。

    2 回复  |  直到 2 年前
        1
  •  3
  •   Ted Lyngmo    2 年前

    您对 matrix_for_det 函数,因为它访问 res 禁止进入您初始化 jn 在错误的地方 jn 将继续增长,远远超过 m-1

    固定:

    void matrix_for_det(double **matrix, int n, int m, double **res, int y, int x) {
        int in = 0;                          // don't initialize `jn`  here
        for (int i = 0; i < n; i++) {
            if (i == y) continue;
            int jn = 0;                      // initialize `jn` here
            for (int j = 0; j < m; j++) {
                if (j == x) continue;
                res[in][jn] = matrix[i][j];
                jn++;
            }
            in++;
        }
    }
    

    …或在 for 回路 init子句

    void matrix_for_det(double **matrix, int n, int m, double **res, int y, int x) {
        for (int i = 0, in = 0; i < n; i++) {
            if (i == y) continue;
            for (int j = 0, jn = 0; j < m; j++) {
                if (j == x) continue;
                res[in][jn] = matrix[i][j];
                jn++;
            }
            in++;
        }
    }
    
        2
  •  2
  •   chux    2 年前

    《准则》有各种各样的问题,其中一个至今尚未指出:

    对齐

    OP试图分配 一旦 存在未定义行为的风险。

    double **matrix = malloc(n * m * sizeof(double) + n * sizeof(double *));
    double *data = double *data = (double *)(matrix + n);;  // Maybe UB
    

    double * 需要指向一个8字节对齐的地址,因为目标对象是 double double ** 指向一个4字节对齐的地址,因为该目标对象是 指针

    什么时候 n 是奇数, (matrix + n) 是一个4字节对齐的值,并将其强制转换为 双重* 导致 未定义的行为 (UB)。

    一种更好的分配和分配方法考虑到可能需要在 matrix[n-1] data


    IMO,只做2个分配。

    如果仍想进行一次分配,请执行 某物 喜欢

    // Illustrative code
    size_t row_size = sizeof(double*) * n;
    size_t padding = 0;
    size_t alignment = alignof(double);
    if (row_size % alignment > 0) {
      padding = alignment - row_size % alignment;
    }
    size_t data_size = sizeof(double) * n * m;  // Note the order to reduce overflow risk.
    double **matrix = malloc(row_size + padding + data_size);
    if (matrix) {
      double *data = (double *)((char *)matrix + row_size + padding);
    

    以上这些本可以做得更优雅,但这是一个炎热的八月下午。该吃点点心了。