I have been writing a piece of code for my coursework in electromagnetic simulation and I have run into a problem. I decided to do a bit extra by expanding the original calculations to really large meshes of up to 10^8 elements, so now I have to use malloc().
So far, so good, but since I prefer to keep my code in libraries and then compile with the inline option of the compiler, I needed a way to pass information between functions. So, I started using structs to keep track of the parameters of the mesh, as well as the pointer to the array of information. I defined the struct the following way:
typedef struct {
int height;
int width;
int bottom; //position of the bottom node
unsigned int*** dat_ptr;//the pointer to the array with all the data
} array_info;
Where the triple pointer to an unsigned int is the pointer to a 2D array. I have to do it this way because otherwise it is passed by value and I cannot change it from within the function.
Now, when I try to allocate memory for the struct with the following function:
void create_array(array_info A)//the function accepts struct of type "array_info" as argument
{
int i;
unsigned int** array = malloc(sizeof(*array) * A.height);//creates an array of arrays
for(i = 0; i<A.height; ++i)
{
array[i] = malloc(sizeof(**array) * A.width);//creates an array for each row
}
*A.dat_ptr=array;//assigns the position of the array to the input pointer
}
I get a segmentation fault upon executing the operation. I cannot see why: sizeof(*A.dat_ptr) is the same as sizeof(array). Thus, in the worst case I should be getting gibberish somewhere down the line, not in the assignment line, right?
You either need to return the array_info
structure (as amended) from the function or (more usually) pass a pointer to the array_info
structure into the function so that the changes you make affect the value in the calling function.
typedef struct
{
int height;
int width;
int bottom;
unsigned int **dat_ptr; // Double pointer, not triple pointer
} array_info;
void create_array(array_info *A)
{
unsigned int **array = malloc(sizeof(*array) * A->height);
for (int i = 0; i < A->height; ++i)
array[i] = malloc(sizeof(**array) * A->width);
A->dat_ptr = array;
}
I assume you do some checking on the memory allocations somewhere; the logical place is this function, though. Recovery from a failure part way through is fiddly (but necessary if you are going to return from the function rather than exit from the program).
void create_array(array_info *A)
{
unsigned int **array = malloc(sizeof(*array) * A->height);
if (array != 0)
{
for (int i = 0; i < A->height; ++i)
{
if ((array[i] = malloc(sizeof(**array) * A->width)) == 0)
{
for (int j = 0; j < i; j++)
free(array[j]);
free(array);
array = 0;
break;
}
}
}
A->dat_ptr = array;
}
The calling function knows that the function failed if the dat_ptr
member is null on return from create_array()
. It might be better to provide a success/failure return value.
I'm using C99, so the calling code might be:
array_info array = { .height = 10, .width = 20, .dat_ptr = 0 };
create_array(&array);
if (array->dat_ptr == 0)
...error handling...
Note that the code in create_array()
might need to check for a null pointer, for negative or zero width or height. I'm not clear what the bottom
element should contain, so I left it uninitialized, which gives me half an excuse for using designated initializers. You can also write the initializer quite clearly without using designated initializers.