views:

354

answers:

5

Hi all,

I d love to know how I can allocate data through a function, and after the function is returned the data is still allocated. This is both for basic types (int, char**) and user defined types. Below are two snipsets of code. Both have the allocation within the function though after the return the allocation goes.

int* nCheck = NULL;
int nCount = 4;

CallIntAllocation(nCheck, nCount);

nCheck[1] = 3; // Not allocated!
...

CallIntAllocation(int* nCheck, int nCount)
{

    nCheck = (int*)malloc(nCount* sizeof(int));
    for (int j = 0; j < nCount; j++)
     nCheck[j] = 0;
}

The same behaviour for as before though for user defined type:

typedef struct criteriatype
{
    char szCriterio[256];
    char szCriterioSpecific[256];
} _CriteriaType;

typedef struct criteria
{
    int nCount;
    char szType[128];
    _CriteriaType* CriteriaType;
} _Criteria;

...
_Criteria* Criteria;
AllocateCriteria(nTypes, nCriteria, Criteria);
...

void AllocateCriteria(int nTypes, int nCriteria[], _Criteria* Criteria)
{
    int i = 0;
    int j = 0;

    Criteria = (_Criteria*)malloc(nTypes * sizeof(_Criteria));

    for (i = 0; i < nTypes; i ++)
    {
     // initalise FIRST the whole structure
     // OTHERWISE the allocation is gone
     memset(&Criteria[i],'\0',sizeof(_Criteria));

     // allocate CriteriaType
     Criteria[i].CriteriaType = (_CriteriaType*)malloc(nCriteria[i] * sizeof(_CriteriaType));

     // initalise them
     for (j = 0; j < nCriteria[i]; j ++)
      memset(&Criteria[i].CriteriaType[j],'\0',sizeof(_CriteriaType));


    }

}

Any ideas? I think I need to pass the pointers as a reference, though how can i do so?

Thanks in advance, Sunscreen

+5  A: 

using return?

Criteria *
newCriteria() {
   Criteria *criteria = malloc(..);
   ...
   return criteria;
}

/* the caller */
Criteria *c1 = newCriteria();
Criteria *c2 = newCriteria();

EDIT

the caller is responsible for calling free()

dfa
For completeness you might want to say a few words about responsibility to free() residing in the caller.
dmckee
yes i used in this way and worked. Thanks
Sunscreen
A: 

You can "return" the pointer to the allocated memory. If NULL is returned, that means the allocation was unsuccessful.

Aditya Sehgal
+3  A: 

You have 2 possible solutions:

 int *CallIntAllocation(int nCount)
 {

     int *nCheck = (int*)malloc(nCount* sizeof(int));
     for (int j = 0; j < nCount; j++)
         nCheck[j] = 0;

     return nCheck;
 }

 int* nCheck = NULL;
 int nCount = 4;

 nCheck = CallIntAllocation(nCount);

or you should pass a pointer to int* if you want to alloc array:

 void CallIntAllocation(int **nCheck, int nCount)
 {

     *nCheck = (int*)malloc(nCount* sizeof(int));
     for (int j = 0; j < nCount; j++)
         *nCheck[j] = 0;
 }

 int* nCheck = NULL;
 int nCount = 4;

 CallIntAllocation(&nCheck, nCount);
Valerii Hiora
Your answer wasn't there when I typed mine :/
Pod
Thanks, i used the return option.
Sunscreen
+3  A: 

To answer your question directly:

int* nCheck = NULL;
int nCount = 4;

CallIntAllocation(&nCheck, nCount);

nCheck[1] = 3; // allocated!
...

void CallIntAllocation(int** pnCheck, int nCount)
{
    int* nCheck = NULL;
    nCheck = (int*) malloc(nCount * sizeof(*nCheck));
    for (int j = 0; j < nCount; j++)
        nCheck[j] = 0;
    *pnCheck = nCheck;
}

but I would suggest this in stead:

nCheck = CallIntAllocation(nCount);

nCheck[1] = 3; // allocated!
...
int *CallIntAllocation(int nCount)
{
    int * nCheck
    nCheck = (int*) malloc(nCount * sizeof(*nCheck));
    for (int j = 0; j < nCount; j++)
        nCheck[j] = 0;
    return nCheck;
}
Pod
Thanks Pod, i did it this way (return int* and _Criteria* respectively) and works.
Sunscreen
+2  A: 

The reason it is not working is that function arguments in C are copied. So, nCheck = NULL in the outside context, you pass it into the CallIntAllocation function and a copy is made. The CallIntAllocation defines it's local copy of nCheck to be the return of the malloc call. But the outer copy is not updated -- it still points at NULL.

The simplest solution is to return the new pointer value and assign it as already suggested by several people.

When you have functions that need to modify data structures, you need to pass around a pointer to them, rather than copies of them, so that the function can modify what the pointer points at. The same principle applies here, although the data structure you want to modify is itself a pointer.

So another solution would be for CallIntAllocation to take a pointer-to-a-pointer which would let you modify where the pointer points, and also dereference it:

CallIntAllocation(int** nCheck, int nCount)
{

    *nCheck = (int*)malloc(nCount* sizeof(int));
    for (int j = 0; j < nCount; j++)
        (*nCheck)[j] = 0;
}

and invocation

CallIntAllocation(&nCheck, nCount);

Clearly in this situation returning a new pointer value is the sensible approach.

Final point: if you have it available, "memset" (C90 but not C89 afaik, part of the single unix specification however) can be used in place of your "for" loop

memset(ncheck, 0, nCount);

(that's for your version of the function, not the one that takes an int ** argument)

jmtd
Thanks all, and thanks jmtd. And i think I can initialise my Struct again by memset: memset(
Sunscreen