views:

182

answers:

6

Hi

I'll go straight to it. I'm working on an assignment, where I suddenly ran into trouble. I have to allocate a struct from within another function, obviously using pointers. I've been staring at this problem for hours and tried in a million different ways to solve it.

This is some sample code (very simplified):

...
some_struct s;
printf("Before: %d\n", &s);
allocate(&s);
printf("After: %d\n", &s);
...

/* The allocation function */
int allocate(some_struct *arg) {

arg = malloc(sizeof(some_struct));
printf("In function: %d\n", &arg);

return 0;
}

This does give me the same address before and after the allocate-call:

Before: -1079752900
In function: -1079752928
After: -1079752900

I know it's probably because it makes a copy in the function, but I don't know how to actually work on the pointer I gave as argument. I tried defining some_struct *s instead of some_struct s, but no luck. I tried with:

int allocate(some_struct **arg)

which works just fine (the allocate-function needs to be changed as well), BUT according to the assignment I may NOT change the declaration, and it HAS to be *arg.. And it would be most correct if I just have to declare some_struct s.. Not some_struct *s. The purpose of the allocation function is to initialize a struct (a some_struct), which also includes allocating it.

One more thing I forgot to mention. The return 0 in the allocate function is reserved for some status messages and therefore I can't return the address using this.

I hope I make sense and some of you out there can help me :P

Thanks in advice.

And thanks guys for formatting my code.

A: 

You can't do it this way. You can't declare a struct by value, and then change it by address.

WhirlWind
Earlz
A: 
some_struct *s;
printf("Before: %d\n", s");
allocate(&s);
printf("After: %d\n", s");
...

/* The allocation function */
int allocate(some_struct **arg) {

*arg = malloc(sizeof(some_struct));
printf("In function: %d\n", *arg");

return 0;
}

You need to modify the pointed value for the struct. So you need another level of indirection, thus you have to send a pointer to the struct pointer.

mcabral
He specified that he's not permitted to do this.
Jason Coco
@Jason, then it is not possible.
Earlz
@Earlz: I think David had the answer his professor was looking for?
Jason Coco
+4  A: 

Typically, I'd return the pointer from allocate:

void * allocate()
{
    void * retval = malloc(sizeof(some_struct));
    /* initialize *retval */
    return retval;
}

If you want to return it in a parameter, you have to pass a pointer to the parameter. Since this is a pointer to a some_struct, you have to pass a pointer to a pointer:

void allocate (some_struct ** ret)
{
    *ret = malloc(sizeof(some_struct));
    /* initialization of **ret */
    return;
}

to be called as

some_struct *s;
allocate(&s);
David Thornley
A: 

Well, C uses pass-by-value, which means that functions get copies of their arguments, and any changes made to those copies don`t affect the original in the caller.

/* The allocation function */
int allocate(some_struct *arg) {

arg = malloc(sizeof(some_struct));
printf("In function: %d\n", &arg");

return 0;
}

Here you pass in the address of your some_struct s. Then you discard that address, and replace it with whatever was returned by malloc. Then you return, and the return value of malloc is lost forever, and you've leaked memory. And your some_struct s has not been changed. It still has whatever random number it was initialized to, which you printed out.

If you may not change the signature of the allocate function, it can never be useful. It must either take the address of a pointer, so that it can modify the value of that pointer, or it must return a pointer that your caller can tuck away.

janks
+1  A: 
int func(some_struct *arg) {
    arg = malloc(sizeof(some_struct));
    ... 
}

Here you just assign the result of malloc to the local arg variable. pointers are passed by value in C, a copy of the pointer gets passed to the function. You cannot change the pointer of the caller this way. Keep in mind the difference in a pointer and what it points to.

You have various options:

Return the pointer from the function:

 some_struct *func(void) {
    arg = malloc(sizeof(some_struct));
    ...
    return arg;
}
...
some_struct *a = func();

Allocate the structure in the caller:

 int func(some_struct *arg) {
    ...
    arg->something = foo;

}
... 
some_struct a;
func(&a);

Or dynamically allocate it

some_struct *a = malloc(sizeof *a);
func(a);

Using a pointer to the callers pointer:

 int func(some_struct **arg) {
    *arg = malloc(sizeof **arg);

}
... 
some_struct *a;
func(&a);

Use a global variable (ugly..)

 some_struct *global;
 int func(void) {
    global = malloc(sizeof *global);

}
 ... 
some_struct *a;
func();
a = global;
nos
+1  A: 

I highly doubt this is what your teacher had in mind, but you can cheat using a series of legal type conversions.

   int allocate(some_struct *arg) 
   /* we're actually going to pass in a some_struct ** instead. 
      Our caller knows this, and allocate knows this.  */
   { 
      void *intermediate = arg;  /* strip away type information */
      some_struct **real_deal = intermediate;  /* the real type */
      *real_deal = malloc(sizeof *real_deal); /* store malloc's return in the 
                                                 object pointed to by real_deal */
      return *real_deal != 0;  /* return something more useful than always 0 */
   }

Then your caller does the same:

   {
      some_struct *s; 
      void *address_of_s = &s; 
      int success = allocate(address_of_s); 
      /* what malloc returned should now be what s points to */
      /* check whether success is non-zero before trying to use it */
   }

This relies on a rule in C that says any pointer to an object can be implicitly converted to a void pointer, and vice-versa, without loss.

Note that formally this is undefined, but it is all but sure to work. While any object pointer value is required to be able to convert to a void* and back without loss, there is nothing in the language that guarantees that a some_struct* can store a some_struct** without loss. But it has a very high likelihood of working just fine.

Your teacher gave you no option but to write formally illegal code. I don't see that you have any other option besides "cheating" like this.

janks
Thank you. This seems to work, tho the code isn't pretty. Yeah, I find it kinda weird as well. The function also has to return 0, as I forgot to mention.But I remember pthreads.. They work like this, as far as I know.You initialize some attributes like this:pthread_attr_t attr;pthread_attr_init(and then you use is as argument to call a pthread_create.This init is allocating the pthread_attr_t struct.
Casper
Vote me up or accept my answer! I need reputation to feel good about myself :(
janks
I have a nagging feeling that the specifications for the assignment got garbled somewhere, either at the teacher's end or Casper's end. I would seriously double-check with the teacher on the intent of the assignment before turning this in.
John Bode
Yeah, I kinda feel that I've misunderstood the assignment.. This problem is just a part of it.Is has to be a some_struct *arg argument. Maybe I'm supposed to alloc the data not from within the function, but then the function has no purpose. Hm.. This is weird.
Casper