代码之家  ›  专栏  ›  技术社区  ›  pasty guacamole

在结构体(C)中使用双指针访问2d数组上的信息

  •  1
  • pasty guacamole  · 技术社区  · 1 年前

    为了理解指针,我基本上创建了一个M[x][y]数组、一个指向所述数组*p_M[x]的指针和一个指向该指针**d_p_M的指针;第一指针指向数组M中一行的第一个元素,双指针指向*p_M中的第一行;

    在函数内部,结果与预期一致,但当试图主要访问元素时,结果会变得很奇怪。

    有人能帮我理解我做错了什么吗?

    以下是相关代码:

    struct Matrix
    {
        int rows;
        int cols;
        double **pMatrix;
    };
    
    struct Matrix unity_matrix(int row, int col);
    
    int main()
    {
        int row = 10, col = 10;
        struct Matrix m1 = unity_matrix(row, col);
        for (int x = 0; x < row; x++)
        {
            printf("\nOutside result %d\n", x);
            for (int y = 0; y < col; y++)
            {
                printf("%lf ", m1.pMatrix[x][y]);
            }
        }
    
        printf("\n Rows = %d Cols = %d", m1.rows, m1.cols);
        return 0;
    }
    
    struct Matrix unity_matrix(int row, int col)
    {
        double v_mtrx[row][col], *p_v_mtrx[row];
    
        for (int x = 0; x < row; x++)
        {
            for (int y = 0; y < col; y++)
            {
                v_mtrx[x][y] = x + y + 1.0;
            }
        }
    
        for (int i = 0; i < row; i++) p_v_mtrx[i] = (double*) v_mtrx + i * col;
    
        struct Matrix mtrx = { row, col, (double **) p_v_mtrx
        };
    
        for (int x = 0; x < row; x++)
        {
            printf("\nInside result %d\n", x);
            for (int y = 0; y < col; y++)
            {
                printf("%lf ", mtrx.pMatrix[x][y]);
            }
        }
    
        return mtrx;
    }
    

    内部结果0

    1.000000 2.000000 3.000000 4.000000

    内部结果1

    2.000000 3.000000 4.000000 5.000000

    内部结果2

    3.000000 4.000000 5.000000 6.000000

    内部结果3

    4.000000 5.000000 6.000000 7.000000

    外部结果0

    1.000000 2.000000 3.000000 4.000000

    外部结果1

    0.000000 -14995397491898438029096961572323840644014499700292909391043299898885044272963634128744240168119533593897190786000787827379354468352.000000 4.000000 0.000000

    外部结果2

    0.000000 0.000000 0.000000 0.000000

    外部结果3

    0.000000 0.000000 0.000000 0.000000

    3 回复  |  直到 1 年前
        1
  •  0
  •   chqrlie    1 年前

    你的方法不起作用,因为指针 pMatrix Matrix 结构返回 unity_matrix 指向本地数组 p_v_mtrx 这个功能。这是一个局部变量,一旦函数返回,它就会被丢弃,因此从 main 具有未定义的行为。

    您应该从堆中分配数组并更改API,以便测试分配错误。

    以下是修改后的版本:

    #include <stdbool.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct Matrix {
        int rows;
        int cols;
        double *values;
        double **pMatrix;
    } Matrix;
    
    bool init_unity_matrix(Matrix *m, int rows, int cols);
    
    void free_matrix(Matrix *m) {
        free(m->values);
        m->values = NULL;
        free(m->pMatrix);
        m->pMatrix = NULL;
    }
    
    int main(void)
    {
        int rows = 10, cols = 10;
        Matrix m;
    
        if (!init_unity_matrix(&m, rows, cols)) {
            printf("cannot allocate matrix\n");
            return 1;
        }
        printf("Unity matrix:\n");
        for (int x = 0; x < m.rows; x++) {
            for (int y = 0; y < m.cols; y++) {
                printf(" %10f", m.pMatrix[x][y]);
            }
            printf("\n");
        }
        free_matrix(&m);
        return 0;
    }
    
    bool init_unity_matrix(Matrix *m, int rows, int cols)
    {
        m->rows = rows;
        m->cols = cols;
        m->values = calloc(sizeof(*m->values), (size_t)rows * cols);
        m->pMatrix = calloc(sizeof(*m->pMatrix), rows);
        if (!m->values || !m->pMatrix) {
            free_matrix(m);
            return false;
        }
        for (int y = 0; y < rows; y++) {
            m->pMatrix[y] = m->values + y * cols;
        }
        for (int y = 0; y < rows; y++) {
            for (int x = 0; x < cols; x++) {
                m->pMatrix[y][x] = y + x + 1.0;
            }
        }
        return true;
    }
    

    这是一种替代方法,其中 矩阵 该结构被分配为单个内存块,并使用C99灵活数组作为行指针数组 pMatrix :

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct Matrix {
        int rows;
        int cols;
        double *pMatrix[];
    } Matrix;
    
    Matrix *allocate_matrix(int rows, int cols)
    {
        // compute size of structure and row array
        size_t size1 = sizeof(Matrix) + rows * sizeof(double *);
        // round up to align values array
        size_t values_offset = (size1 + sizeof(double) - 1) / sizeof(double);
        size_t total_size = sizeof(double) * (values_offset + (size_t)rows * cols);
        Matrix *m = calloc(1, total_size);
        if (m) {
            double *values = (double *)m + values_offset;
            m->rows = rows;
            m->cols = cols;
            for (int y = 0; y < rows; y++) {
                m->pMatrix[y] = values + y * cols;
            }
        }
        return m;
    }
    
    Matrix *unity_matrix(int rows, int cols)
    {
        Matrix *m = allocate_matrix(rows, cols);
        if (m) {
            for (int y = 0; y < rows; y++) {
                for (int x = 0; x < cols; x++) {
                    m->pMatrix[y][x] = y + x + 1.0;
                }
            }
        }
        return m;
    }
    
    void free_matrix(Matrix *m)
    {
        free(m);
    }
    
    int main(void)
    {
        int rows = 10, cols = 10;
        Matrix *m = unity_matrix(rows, cols);
        if (!m) {
            printf("cannot allocate matrix\n");
            return 1;
        }
        printf("Unity matrix:\n");
        for (int x = 0; x < m->rows; x++) {
            for (int y = 0; y < m->cols; y++) {
                printf(" %10f", m->pMatrix[x][y]);
            }
            printf("\n");
        }
        free_matrix(m);
        return 0;
    }
    
        2
  •  0
  •   Chris    1 年前

    如注释中所述,2D数组与指针数组不同。你还需要考虑寿命。在函数中声明的数组具有自动生存期,在函数返回后不再有效。

    为了适应这种情况,你需要 动态 在结构体中分配数组。 检查以确保 malloc 成功,如果没有成功,则清理代码。

    例如。

    struct Matrix *allocMatrix(int rows, int cols) {
        struct Matrix *m = malloc(sizeof(struct Matrix));
        if (!m) return NULL;
    
        m->rows = rows;
        m->cols = cols;
    
        m->pMatrix = malloc(sizeof(double *) * rows);
        if (!m->pMatrix) {
            free(m);
            return NULL;
        }
    
        for (int i = 0; i < rows; i++) {
            m->pMatrix[i] = malloc(sizeof(double) * cols);
            if (!m->pMatrix[i]) {
                // Free the previously successfully allocated rows. 
                for (int j = 0; j < i; j++) {
                    free(m->pMatrix[j]);
                }
    
                free(m);
                return NULL;
            }
        }
            
        return m;
    }
    

    通过返回一个指向该结构的指针,而不是按值返回该结构,您可以:

    1. 避免不必要地复制大量数据。
    2. 返回 NULL 以指示分配失败。

    您会发现将问题分解为高度集中的函数很有用。一个函数用于分配如上所示的矩阵,另一个函数则用于获取正确分配的矩阵并填写。这样,您就可以单独调试分配,而不必担心放入其中的数据。

        3
  •  0
  •   Vlad from Moscow    1 年前

    为了更清楚,让我们首先详细说明函数中发生了什么 unity_matrix .

    在本声明中

    double v_mtrx[row][col], *p_v_mtrx[row];
    

    这相当于以下声明

    double v_mtrx[row][col];
    double * p_v_mtrx[row];
    

    声明了两个数组:二维可变长度数组 v_mtrx 一维可变长度数组 p_v_mtrx 带元素类型 double * .

    在此之后for循环

    for (int x = 0; x < row; x++)
    {
        for (int y = 0; y < col; y++)
        {
            v_mtrx[x][y] = x + y + 1.0;
        }
    }
    

    阵列 v_mtrx 看起来像

    1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0
    2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0
    3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0
    4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0
    5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0
    and so on
    

    在这个for循环中

    for (int i = 0; i < row; i++) p_v_mtrx[i] = (double*) v_mtrx + i * col;
    

    二维数组 v_mtrx 被重新解释为具有元素类型的一维数组 double .

    现在,具有指针类型的元素是怎样的 双倍* 阵列的 p_v_mtrx 初始化?

    由于指针算法 p_v_mtrx[0] 设置为 &v_mtrx[0][0] , p_v_mtrx[1] 设置为 &v_mtrx[1][0] , p_v_mtrx[2] 设置为 &v_mtrx[2][0] 等等。

    在此结构类型的对象声明中

    struct Matrix mtrx = { row, col, (double **) p_v_mtrx
    };
    

    初始化器列表等效于以下内容

    struct Matrix mtrx = { row, col, p_v_mtrx
    };
    

    因为表达式中使用的数组(很少有例外)会隐式转换为指向其第一个元素的指针。

    作为数组的元素类型 p_v_mtrx 双倍* 则指向其第一个元素的指针将具有以下类型 double ** .

    现在让我们考虑一下这些嵌套的for循环中发生了什么

    for (int x = 0; x < row; x++)
    {
        printf("\nInside result %d\n", x);
        for (int y = 0; y < col; y++)
        {
            printf("%lf ", mtrx.pMatrix[x][y]);
        }
    }
    

    表达 pMatrix[0] 生成数组的第一个元素 p_v_mtrx 这反过来又指向了元素 v_mtrx[0][0] 由于其由表达式初始化 &v_mtrx[0][0] 如上所示。因此,由于指针算法 x 等于 0 二维数组的第一行 v_mtrx 这就是表达式的值 mtrx.pMatrix[0][y] 对应于表达式的值 v_mtrx[0][y] 被输出。以此类推,例如表达式的值 mtrx.pMatrix[1][y] 对应于表达式的值 v_mtrx[1][y] 被输出。

    现在注意数组 v_mtrx p_v_mtrx 是具有自动存储持续时间的本地阵列,退出finction后将不会进行校准。因此,在退出函数后取消对这些数组的指针引用会调用未定义的行为。

    您需要动态分配数组,如其他答案所示。