tags:

views:

161

answers:

6

Suppose I have the following struct and function returning a pointer:

typedef struct {
  int num;
  void *nums;
  int size;
} Mystruct;

Mystruct *mystruct(int num, int size)
{ 
   //Is the following correct? Is there a more efficient way?
   Mystruct mystruct;
   mystruct.num = num;
   mystruct.size = size;
   mystruct.nums = malloc(num*sizeof(size));
   Mystruct *my;
   *my = mystruct;
   return my;
}

I want to define any Mystruct pointer using the above function. Should I declare a Mystruct variable, define the properties of Mystruct, assign a pointer to it, and return the pointer or define the properties of a mystruct property through a pointer immediately?

+5  A: 

Should I declare a Mystruct variable, define the properties of Mystruct, assign a pointer to it, and return the pointer

Definitely not, because the variable defined in the function (in "auto" storage class) will disappear as the function exits, and you'll return a dangling pointer.

You could accept a pointer to a Mystruct (caller's responsibility to allocate that) and fill it in; or, you can use malloc to create a new one (caller's responsibility to free it when it's done). The second option at least lets you keep the function signature you seem to be keen on:

Mystruct *mystruct(int num, int size)
{
   Mystruct *p = (Mystruct *) malloc(sizeof(MyStruct));
   ....
   return p;
}

but it's often an inferior one -- since the caller has to have responsibilities anyway, may as well go with the first option and potentially gain performance (if the caller can use an auto-class instance because it knows the scope of use is bounded).

Alex Martelli
why did you typecast malloc with (Mystruct*)? Is that necessary?
idealistikz
@idealistikz: No, it's not necessary.
sth
It is not necessary in C (and is often considered bad style in that language) - but the converse is true in C++.
caf
+1  A: 

Allocating a new Mystruct and returning a pointer to it would usually look more or less like this:

Mystruct *mystruct(int num, int size)
{
   Mystruct *result;

   result = malloc(sizeof(MyStruct));
   if (!result)
     return NULL;

   result->num = num;
   ...

   return result;
}

Later, when you are done with the Mystruct allocated here with malloc, it should be freed again with free().

Just declaring a local variable and returning a pointer to that local variable will not work. The local variable goes out of scope at the end of the function and the memory where it was stored is most likely reused for other purposes. The returned pointer will still point to the memory location where the local variable once was, but since that variable doesn't exist anymore, that pointer wouldn't be of much use.

sth
+1  A: 

It's important to remember that the pointer is not something you assign to the structure, but rather the pointer indicates the location in memory that you wish to treat as a structure. Based on your question, you really want to allocate the memory to hold the data structure. This gives you a pointer to the memory location allocated. Once you have that, you can return it.


EDIT (after edit to original question) Looking at your edit to the question, you definitely will have problems with the "my" pointer. This is uninitialised, and may point to anywhere in memory. When you attempt to copy the structure to it, you'll probably get a seg-fault.

graza
+1  A: 

You can't use the variable because it will be deallocated when the function exits. For example:

Mystruct *mystruct(int num, int size)
{
   MyStruct x;
   x.num = 1;
   ...
   return &x;
}

Will give a segmentation fault or access violation because the memory for x is deallocated as soon as you exit. So you have to allocate memory for the struct (and be sure to free it up later) or declare a global that will stay around for ever. Example for the latter...

Mystruct *mystruct(int num, int size)
{
   MyStruct *x;
   x = (MyStruct*)malloc( sizeof( MyStruct ) );
   x->num = 1;
   ...
   return x;
}
staticman
Alex Martelli
staticman
A: 

One more way to do it..

int mystruct(Mystruct *mystruct, int num, int size){
   if(mystruct == NULL)
      return -1;

   mystruct->num = num;
   mystruct->size = size;
   ::
   return 0;
}

int main(){
   Mystruct my;

   if(mystruct(&my, 3, 4) != 0){
      fprintf(stderr, "Cannot init!\n");
      exit(0);
   }
   ::
}
N 1.1
A: 

If you are writing generic code and you don't know how it may be used, it's good to provide both options:

int mystructm(Mystruct *storage, int num, int size)
{
    int rv = 0;

    storage->num = num;
    storage->size = size;
    storage->nums = malloc(num*sizeof(size));
    if (!storage->nums)
        return -1;

    return 0;
}

Mystruct *mystruct(int num, int size)
{
    Mystruct *mp = (Mystruct *)malloc(sizeof(Mystruct));
    if (mp)
    {
        if (mystructm(mp, num, size) == -1)
        {
            free(mp);
            mp = NULL;
        }
    }

    return mp;
}

The idea is that as a library writer, you shouldn't dictate policy (such as that each Mystruct must be dynamically allocated) but should let the application writer decide that.

R Samuel Klatchko