Reputation: 4203
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
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
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
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
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