arrayscmultidimensional-arrayimplicit-conversionpointer-to-pointer

Accessing information on a 2d array with a double pointer in a struct (C)


Trying to understand pointers, basically i created a M[x][y] array, a pointer to said array *p_M[x] and a pointer to said pointer **d_p_M; the first pointer point to the first element of a row in the array M, and the double pointer points to the first row in *p_M;

Inside the function the results are as expected, but when trying to accessing the elements in main the results get funky.

Can anyone help me understand what i'm doing wrong please?

Below is the relevant code:

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;
}

Inside result 0

1.000000 2.000000 3.000000 4.000000

Inside result 1

2.000000 3.000000 4.000000 5.000000

Inside result 2

3.000000 4.000000 5.000000 6.000000

Inside result 3

4.000000 5.000000 6.000000 7.000000

Outside result 0

1.000000 2.000000 3.000000 4.000000

Outside result 1

0.000000 -14995397491898438029096961572323840644014499700292909391043299898885044272963634128744240168119533593897190786000787827379354468352.000000 4.000000 0.000000

Outside result 2

0.000000 0.000000 0.000000 0.000000

Outside result 3

0.000000 0.000000 0.000000 0.000000


Solution

  • Your approach does not work because the pointer pMatrix of the Matrix structure returned by unity_matrix points to the local array p_v_mtrx of this function. This array has automatic storage (it is a local variable) and it is discarded as soon as the function returns, so accessing it from main has undefined behavior. The pointers in this array point to the elements of the 2D array of double, itself a local object in the scope of the function, hence discarded as well when the function returns.

    You must allocate the arrays from the heap and change the API so you can test for allocation error.

    Here is a modified version:

    #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;
    }
    

    Here is an alternate approach where the Matrix structure is allocated as a single memory block and uses a C99 flexible array for the row pointer array 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;
    }