Распределить данные через функцию (ANSI C) - PullRequest
1 голос
/ 15 июня 2009

Я хотел бы знать, как я могу распределять данные через функцию, и после того, как функция возвращена, данные все еще распределены. Это как для базовых типов (int, char **), так и для пользовательских типов. Ниже приведены два фрагмента кода. Оба имеют распределение внутри функции, хотя после возврата распределение продолжается.

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;
}

То же поведение, что и раньше, но для определенного пользователем типа:

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));


    }

}

Есть идеи? Я думаю, что мне нужно передать указатели в качестве ссылки, хотя, как я могу это сделать?

Заранее спасибо, Солнцезащитный

Ответы [ 5 ]

5 голосов
/ 15 июня 2009

используя возврат?

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

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

EDIT

вызывающий абонент отвечает за вызов free ()

4 голосов
/ 15 июня 2009

У вас есть 2 возможных решения:

 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);

или вы должны передать указатель на int *, если хотите выделить массив:

 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); 
3 голосов
/ 15 июня 2009

Чтобы ответить на ваш вопрос напрямую:

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;
}

но я бы предложил это вместо:

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;
}
2 голосов
/ 15 июня 2009

Причина, по которой он не работает, заключается в том, что аргументы функции в C копируются. Итак, nCheck = NULL во внешнем контексте, вы передаете его в функцию CallIntAllocation, и создается копия. CallIntAllocation определяет, что его локальная копия nCheck является возвращением вызова malloc. Но внешняя копия не обновляется - она ​​по-прежнему указывает на NULL.

Самое простое решение - вернуть новое значение указателя и назначить его, как уже было предложено несколькими людьми.

Когда у вас есть функции, которые должны изменять структуры данных, вам нужно передавать указатель на них, а не их копии, чтобы функция могла изменить то, на что указывает указатель. Здесь применяется тот же принцип, хотя структура данных, которую вы хотите изменить, сама по себе является указателем.

Таким образом, другое решение было бы для CallIntAllocation взять указатель на указатель, который позволил бы вам изменять, куда указывает указатель, а также разыменовывать его:

CallIntAllocation(int** nCheck, int nCount)
{

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

и вызов

CallIntAllocation(&nCheck, nCount);

Очевидно, что в этой ситуации возврат нового значения указателя является разумным подходом.

Конечный пункт: если он у вас есть, вместо цикла «for» можно использовать «memset» (C90, но не C89 afaik, однако часть спецификации unix)

memset(ncheck, 0, nCount);

(это для вашей версии функции, а не для той, которая принимает аргумент int **)

0 голосов
/ 15 июня 2009

Вы можете «вернуть» указатель на выделенную память. Если возвращается значение NULL, это означает, что распределение было неудачным.

...