Search code examples
cmalloccalloc

Generating a matrix with malloc and calloc causing confusing behavior


I've been working on a little project that deals with matrix computations in C.

I was doing some testing against the code I wrote and came across some incredibly confusing behaviour.

Before I get into the question, here is some of the relevant code.

Matrix Definition:

typedef struct
{
    double **matrix;
    int rows;
    int cols;
    int dimensions[2];
    char *str_dims;
} Matrix;

When I initialize a matrix I allocate memory using malloc for the number of rows, then iterate over the rows allocating memory for all of the columns using calloc so they are 0 initialized.

void init_matrix(Matrix *x, int i, int j)
{
    x->matrix = malloc(x->rows * sizeof(double *));
    for (int i = 0; i < x->rows; i++)
        x->matrix[i] = calloc(x->cols, sizeof(double));
}

I also have the functionality of generating a random matrix,

Matrix get_rand_matrix(int i, int j)
{
    Matrix x;
    init_matrix(&x, i, j);
    srand(time(NULL));
    for(int i = 0; i < x.rows; i++)
        for(int j = 0; j < x.cols; j++)
            x.matrix[i][j] = rand();
    return x;
}

Confusing Behaviour

Asides from the code most likely being pretty horrendous by the standards of the ancients, I had thought that it was working properly. However, by luck of the draw, when I was doing some testing (printing the matrix) I mindlessly incremented the loop responsible for iterating over the columns of the matrix by 1 and this was the output that I received. (Formatted for your viewing pleasure.)

+-------------+--------------+-----+
|739979002.00 | 1854570721.00| 0.00|                                                                                                                                                    
|130427701.00 | 402893063.00 | 0.00|                                                                                                                                                     
|1973118592.00| 135400441.00 | 0.00|                                                                                                                                                    
|1707001127.00| 1093842609.00| 0.00|
+----------------------------------+

Where the expected output would have been,

+-------------+--------------+
|739979002.00 | 1854570721.00|                                                                                                                                                    
|130427701.00 | 402893063.00 |                                                                                                                                                     
|1973118592.00| 135400441.00 |                                                                                                                                                    
|1707001127.00| 1093842609.00|
+----------------------------+

The code that generated this just to keep you out of the dark,

Matrix m = get_rand_matrix(4, 2);
for(int i = 0; i < m.rows; i++)
{
    for(int j = 0; j < m.cols + 1; j++)
        printf("%.2lf ", m.matrix[i][j]);
    printf("\n");
}

Question

Now, I have honestly no idea why I am not getting a segfault and have access to zero-initialized elements outside of (what I would have thought to be) the bounds of the memory that was allocated. I can only assume its an error on my part by conflating malloc and calloc together, but then again, I also don't see why this wouldn't work.

Does anyone know what is going on, why are there 0 initialized doubles outside of the bounds of the allocated memory? I am rather new to C and memory allocation in general and this has completely dazzled me.

Interesting addition

Some of the elements seem to repeat when you increase the column loop exiting condition (Generated with j < m.cols + 5)

+-------------------------------------------------------------------------------+
|549092153.00 | 1317836633.00 | 0.00| 0.00 | 218607745.00 |1326282480.00 | 0.00 |                                                                                                               
|218607745.00 | 1326282480.00 | 0.00| 0.00 | 715372192.00 |976468777.00  | 0.00 |                                                                                                               
|715372192.00 | 976468777.00  | 0.00| 0.00 | 103851159.00 |363785358.00  | 0.00 |                                                                                                                
|103851159.00 | 363785358.00  | 0.00| 0.00 |     0.00     | 0.00         | 0.00 |
+-------------------------------------------------------------------------------+

I increased the increment to +1000 and it still continues to print 0.00 and repeated numbers.


Solution

  • If I am reading this correctly, what you are doing is simply reading memory outside of the allocated arrays. This may cause a segmentation fault, but not always. This site gives some examples: https://www.geeksforgeeks.org/accessing-array-bounds-ccpp/

    In general, when you ask C to read from memory, you give it a physical address. If that address exists and you are allowed to read from it, it will give you back the data stored there regardless of whether it is in your array. These could be 0 values or garbage data from variables you or other programs have written there. In your last example where you see the repeating values, it's because your program allocated memory for matrix[i] and matrix[i+1] consecutively. Therefore, when you start to read past the bounds of matrix[i], you start to read the memory from matrix[i+1].

    Also, you mentioned that it was storing doubles outside of the bounds, but that's not quite correct. C does not know what type of data was supposed to be stored there, it only knows to interpret it as doubles because you told it to. If you cast everything as chars, it will read and print chars instead of doubles.