Как правильно освободить структуру, возвращаемую из функции? - PullRequest
0 голосов
/ 02 мая 2018

Я новичок в C и пытаюсь выяснить, как избавиться от структур, ссылки на которые возвращаются из функции.

Например, это примерно то, что я хочу сделать.

typedef struct test_t{
    char *test_c;
} test_t;

int testFunc(test_t** output){
    test_t* testStruct = malloc(sizeof(testStruct));
    char* buf = malloc(sizeof(char)  * 5);
    strcpy(buf, "test");
    testStruct->test_c = buf;
    *output = testStruct;
    return 0;
}

int main() {
    test_t* test;
    testFunc(&test);
    printf("%s\n",test->test_c);
    free(test);
    return 0;
}

Итак, в основном я получаю тестовую структуру. После printf (предположим, что после подопечных есть какой-то код), он мне больше не нужен, и я хочу освободить его. Но как это сделать правильно? Должен ли я сначала просто освободить test_c? Но что, если он не был выделен?

Ответы [ 5 ]

0 голосов
/ 02 мая 2018

У вас должна быть процедура, которая выделяет и инициализирует экземпляр test_t. В C ++ или других объектно-ориентированных языках это будет конструктор. В C вы должны бросить свой собственный - что-то похожее на следующее:

test_t *create_test_t(char *init_test_c)
  {
  test_t *tt;

  tt = malloc(sizeof(test_t));
  if(init_test_c != NULL)
    {
    tt->test_c = malloc(strlen(init_test_c)+1);
    strcpy(tt->test_c, init_test_c);
    }
  else
    tt->test_c = NULL;
  }

Вы также захотите, чтобы функция правильно избавлялась от экземпляра test_t. Что-то вроде:

void destroy_test_t(test_t *tt, bool static)
  {
  free(tt->test_c);

  if(!static)
    free(tt);
  }

Параметр static включен для контроля того, должен ли tt быть свободным или нет. Очевидно, что вы не хотите пытаться освободить указатель на статический экземпляр test_t, в этом случае вы передадите TRUE для static. Для динамически распределяемых экземпляров test_t, например, созданные с помощью процедуры create_test_t, вы получите FALSE для static.

Затем вы используете эти функции каждый раз, когда вам нужно создать или уничтожить test_t.

Удачи.

0 голосов
/ 02 мая 2018

Должен ли я сначала просто освободить test_c?

Да, сначала вы должны освободить test_c, потому что, если вы освободите test, вы потеряете ссылку на test_c.

Но что, если он не был выделен?

Когда вы возвращаете int из testFunc, вы можете использовать его как состояние выделения вашей структуры (хотя, имхо, возвращение указателя было бы лучшей идеей):

int testFunc(test_t** output){
    test_t* testStruct = malloc(sizeof(testStruct));
    if (testStruct == NULL)
    /* testStruct allocation failure. */
         return -1;
    char* buf = malloc(sizeof(char)  * 5);
    if (buf == NULL) {
    /* buf allocation failure. */
         free(testStruct);
         return -1;
    }
    strcpy(buf, "test");
    testStruct->test_c = buf;
    *output = testStruct;
    return 0;
}

После этого вы проверяете testFunc ошибка:

/* Check for allocation failure. */
if (testFunc(&test) == -1) {
    fprintf(stderr, "Allocation error");
    exit(EXIT_FAILURE);
}
/* Allocation succeeded. */
printf("%s\n",test->test_c);
free(test->test_c);
free(test)
0 голосов
/ 02 мая 2018

Но как это сделать правильно? Должен ли я сначала просто освободить test_c? Но что, если он не был выделен?

Основное правило: если вы пишете функцию, которая выделяет ресурс, напишите дополнительную, которая освобождает его. Так что применительно к вашему примеру:

void testFree(test_t* toFree) {
    free(toFree->test_c);
    free(toFree);
}

... а потом ...

printf("%s\n",test->test_c);
testFree(test);

Если testFree реализован и выставлен вместе с testFunc, вызывающим абонентам не нужно знать детали вашей реализации. Сами функции, будучи частью одной и той же библиотеки, обеспечат правильное поддержание инвариантов. Если вы когда-нибудь переключите схемы распределения, не нужно менять код вызова. Вы просто изменили бы testFree, чтобы согласиться с новым testFunc и снова связать.

0 голосов
/ 02 мая 2018

Во-первых, вам нужно освободить память, выделенную для char в структуре, если вы ее инициализировали. В противном случае вы получите сообщение об ошибке, поэтому перед любой свободной инструкцией вы должны проверить, равен ли NULL указатель, в котором хранится позиция памяти для test_p, который вы пытаетесь освободить. Кроме того, бесплатная инструкция должна принимать * test в качестве параметра, а не test.

0 голосов
/ 02 мая 2018

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

typedef struct test_t{
    char *test_c;
} test_t;

int testFunc(test_t** output){
    test_t* testStruct = malloc(sizeof(testStruct));
    char* buf = malloc(sizeof(char)  * 5);
    strcpy(buf, "test");
    testStruct->test_c = buf;
    *output = testStruct;
    return 0;
}

int main() {
    test_t* test;
    testFunc(&test);
    printf("%s\n",test->test_c);
    free(test->test_c);
    free(test);
    return 0;
}

Если ваша структура не выделяет все свои элементы, вы можете установить их указатель на NULL, который после передачи на free (3) будет эквивалентен бездействию.

...