I have a struct that will contain some dynamic-allocated array.
I have written the following code, and it works but I do not understand why it does work.
#include <stdio.h>
#include <stdlib.h>
struct Tray {
int *parr;
};
int allocateTray(int n, struct Tray *tray) {
tray->parr = calloc(n, sizeof(*tray->parr));
for (int i = 0; i<n; i++) {
tray->parr[i] = i+1;
}
}
int main() {
struct Tray tray = {NULL}, *ptray;
int n;
n = 5;
ptray = &tray;
allocateTray(n, ptray);
for (int i = 0; i<n; i++) {
printf("ptray->parr[%d] = %d \n", i, ptray->parr[i]);
}
return 0;
}
With a array (not inside a struct), even if I allocate arr
inside a function with argument int *arr
, it does not give main
the allocated array, and it forces one to use double pointer there.
But in this case, I just used a pointer to a struct, and it worked. I was thinking that I should use something like double pointer to a struct.
Why in this case it works only with a single pointer?
"even if I allocate arr inside a function with argument int *arr, it does not give main the allocated array"
In general, whatever object a function will be expected to modify when passed via it's parameter list requires that the object's address be passed, not the object itself. To illustrate, for any type T:
For T s
if s
is to be changed, argument of function prototype should be; void func(T *s);
With calling example being
T s = 0;
func(&s);
For T *s
if *s
is to be changed, argument of function prototype should be; void func(T **s);
With calling example being
T *s = 0;
func(&s);
For T **s
if **s
is to be changed, argument of function prototype should be; void func(T ***s);
With calling example being
T **s = 0;
func(&s);
And so on... (Note the apparent similarity in calling convention for each.)
Example - the following code will fail to change the value of its argument:
int main(void)
{
int x = 0;//object to be changed
change_x(x);//passing object directly via argument
//x is returned unchanged
return 0;
}
void change_x(int x)
{
x = 10;//within this function only will x now contain 10
}
But this example passes address and is able to change the value:
int main(void)
{
int x = 0;//object to be changed
change_x(&x);//passing the address of the object to be changed
return 0;
}
void change_x(int *x)
{
*x = 10;//access to the object via its address allows change to occur
}
"I was thinking that I should use something like double pointer to a struct."
Yes, as an argument in a function prototype that would work when needing to change the contents of memory pointed to by a pointer object.
With a pointer (to any object) that needs to be modified in a function, the same is true, its address must be passed, not the pointer itself. This then would require the argument for that function to accommodate a pointer to a pointer. A simple example using a struct
with int
members as well as a int *
member:
typedef struct {
int a;
int b;
int *parr;
}val_s;
void change_val(val_s **v, size_t num_parr);
int main(void)
{
val_s *val = NULL;
int num = 10;
change_val(&val, num);//passing address to a pointer
val->a = 10;
val->b = 20;
for(int i = 0;i < num; i++) val->parr[i] = i;
//once finished using memory,
//free it in the reverse order in which it was allocated
free(val->parr);
free(val);
return 0;
}
void change_val(val_s **v, size_t num)//note only top level pointer address needs be send
{ //member pointers, whether allocated or not are
(*v) = malloc(sizeof(val_s)); //relative to memory of top level object
if(*v)
{
(*v)->parr = malloc(num*sizeof (*v)->parr);//allocate memory to member pointer
}
}