wolfPack88
wolfPack88

Reputation: 4203

Creating *** object (pointer to pointer to pointer) in C

I am trying to write a function that creates a contiguous block of memory and assigns it to a 3d array. The code works in that it allows me to use the memory, and, when I use data stored in objects created with this function, the results appear correct. However, when I try to free the memory I have allocated with this function, I immediately get a glibc error. Here is the function:

void *** matrix3d(int size, int rows, int cols, int depth) {
    void ***result;

    int col_size = depth * size;
    int row_size = (sizeof(void *) + col_size) * cols;
    int data_size = (rows * cols * depth + 1) * size;
    int pointer_size = rows * sizeof(void **) + cols * sizeof(void *);
    int i, j;
    char *pdata, *pdata2;

    if((result = (void ***) malloc(pointer_size + data_size)) == NULL)
            nerror("ERROR: Memory error.\nNot enough memory available.\n", 1);

    pdata = (char *) result + rows * sizeof(void **);

    if((long) pdata % (col_size + sizeof(void *)))
            pdata += col_size + sizeof(void *) - (long) pdata % (col_size + sizeof(void *));

    for(i = 0; i < rows; i++) {
            result[i] = pdata;

            pdata2 = pdata + cols * sizeof(void *);

            for(j = 0; j < cols; j++) {
                    result[i][j] = pdata2;
                    pdata2 += col_size;
            }

            pdata += row_size;
    }

    return result;
}

It is called in this manner:

double ***positions = (double ***) matrix3d(sizeof(double), numResidues, numChains, numTimesteps);

for(i = 0; i < numResidues; i++)
    for(j = 0; j < numChains; j++)
        for(k = 0; k < numTimesteps; k++)
             positions[i][j][k] = 3.2;

free(positions);

What have I done wrong? Thank you for the help.

Upvotes: 0

Views: 121

Answers (4)

Jens Gustedt
Jens Gustedt

Reputation: 78903

things like this are magnificant candidates to get things wrong

pdata = (char *) result + rows * sizeof(void **);

there is no reason at all to circumvent the address computation that the compiler does for you.

Upvotes: 0

David Schwartz
David Schwartz

Reputation: 182753

Please excuse my dear Aunt Sally.

int data_size = (rows * cols * depth + 1) * size;

This should be:

int data_size = (rows * cols * (depth + 1)) * size;

Running the code under valgrind identified the error immediately.

Upvotes: 1

Some programmer dude
Some programmer dude

Reputation: 409166

What you are doing is one single allocation and then casting it to a tripple-pointer, meaning you have to deal a lot with offsets.

It would probably be better to a larger number of allocations:

char ***result = malloc(sizeof(char **) * rows);

for(i = 0; i < rows; i++) {
    result[i] = malloc(sizeof(char *) * cols);

    for(j = 0; j < cols; j++) {
        result[i][j] = malloc(sizeof(char) * size);

        /* Copy data to `result[i][j]` */
    }
}

When freeing, you have to free all of the allocations:

for(i = 0; i < rows; i++) {
    for(j = 0; j < cols; j++) {
        free(result[i][j]);
    }

    free(result[i]);
}

free(result);

Upvotes: 0

cnicutar
cnicutar

Reputation: 182619

What have I done wrong?

Your code is hard to follow (you're playing with pdata a lot) but 99% you're writing past the allocated space and you're messing up the bookkeeping left by glibc.

I can use the data I've written just fine. The only issue is when I try to use free.

That's because glibc only gets a chance to see you messed up when you call it.

Upvotes: 3

Related Questions