освобождает подстроку, не делая double free в c - PullRequest
1 голос
/ 07 июля 2010

Вчера я задал похожий вопрос относительно того, как free выделить память для sub-string. Теперь у меня есть еще один вопрос, касающийся той же проблемы (включая набор условий), как я могу освободить следующую подстроку, не делая double free?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct st_ex {
    char product[16];
    float price;
};
struct st_temp {
    char *prod;
};

char *temp = NULL;

// from stackoverflow
char* substr( const char* source, size_t start, size_t end )
{
    char* dest = malloc( end - start + 1) ;
    memcpy( dest, &source[start], end - start ) ;
    dest[end - start] = 0 ;
    return dest ;
}

int main()
{
    struct st_ex structs[] = {{"mp3 player", 2.0f}, {"plasma tv", 20.0f},
                              {"notebook", 10.0f},  {"smartphone", 49.9f},
                              {"dvd player", 10.0f}, {"matches", 0.2f }};
    struct st_temp **temp_struct;

    size_t j, i;
    temp_struct = malloc(sizeof *temp_struct * 6);
    for (j = 0; j < 6; j++)
        temp_struct[j] = malloc(sizeof *temp_struct[j]);

    size_t structs_len = sizeof(structs) / sizeof(struct st_ex);

    // NOTE: that structs_len may vary in size - not just 6 
    for(i=0; i<structs_len; i++){
        if (i == 0)
            temp_struct[i]->prod = "+";
        else if(i == 1)
            temp_struct[i]->prod = "Bar";
        else if(i == 5)
            temp_struct[i]->prod = "Foo";
        else {
            temp = substr(structs[i].product, 0, 4);
            temp_struct[i]->prod = temp;
        }
    }
    for(i=0; i<6; i++ )
        printf("%s\n",temp_struct[i]->prod);

    for(i = 0; i < 6; i++ ){
        /* can I do something like this? */
        /*if (i != 0 || i != 1 || i != 5)*/
        free(temp_struct[i]->prod);
        free(temp_struct[i]);
    }
    free(temp_struct);
    return 0;
}

Ответы [ 5 ]

1 голос
/ 07 июля 2010

Проблема в том, что иногда вы устанавливаете temp_struct[i]->prod в строку в кавычках ("Bar"), которую вы не можете освободить, а иногда в результат вызова substr, который вы должны освободить.

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

  temp_struct[i]->prod = new_string("Bar");

где

char* new_string( const char* source )
{
    char* dest = malloc( strlen(source) + 1 ) ;
    strcpy(dest, source);        
    return dest ;
}

или, вы должны следить, если вам нужно освободить или нет

 struct st_temp {
     char *prod;
     int prod_must_be_freed;
 };

установите для prod_must_be_freed значение 0 или 1 и проверьте это перед освобождением.

И, наконец, все было бы улучшено за счет использования функций для манипулирования этими структурами, а не просто возиться с ними напрямую. Затем вы могли бы сделать free_st_temp(st_temp*), который проверял, должен ли prod быть освобожден, а затем освободил структуру. Ваш цикл будет

for(i = 0; i < 6; i++ ){    
    free_st_temp(temp_struct[i]);
}
0 голосов
/ 07 июля 2010

У вас есть дополнительная проблема. Когда вы делаете temp_struct[i]->prod = "Bar"; Вы назначаете const char * для prod. Этот указатель не может быть освобожден (наиболее вероятный результат - сбой). Поэтому, если вы хотите, чтобы ваш код был настроен таким образом, чтобы prod мог указывать либо на динамическую память, которую вы получили из malloc, либо на постоянный строковый литерал, вам также нужно отслеживать, какой он есть, и только освобождать динамическую память .

Условие в вашем комментарии будет работать технически, но будет очень плохой формой. Лучшая идея - не смешивать и сопоставлять строковые типы в одном и том же указателе. Но если вы настаиваете на том, чтобы делать это таким образом, то улучшением было бы добавление еще одной переменной в вашу структуру, которая устанавливается в значение true, когда prod должен быть освобожден, и в значение false, если это не так.

0 голосов
/ 07 июля 2010

Подстроки не занимают дополнительной памяти. Они являются указателями на части уже существующих строк.

0 голосов
/ 07 июля 2010

Да, учитывая, что ваш substr выделяет память для подстроки с malloc, разумно (необходимо, действительно) free этой памяти, когда вы закончите с ней. Тем не менее, я думаю, что то, что вы делаете сейчас, чрезвычайно хрупко и подвержено ошибкам (мягко говоря). Если у вас есть какой-либо выбор, я бы выделил строки для всех prod членов таким же образом - если вы не можете распределить их все статически, то распределите их все динамически, поэтому, когда Вы освобождаете структуры, вы можете делать это равномерно. Попытка обеспечить соответствие подписчиков свободным prod тогда и только тогда, когда они были выделены динамически, практически напрашивается на неприятности.

0 голосов
/ 07 июля 2010

Да, хотя вы захотите раскомментировать это if и изменить условия в if для объединения с && вместо || (в противном случае это всегда будет истина - каждое число либоне равно нулю или не равно единице!)

Подстроки, сохраненные в temp_struct[i]->prod для i, отличных от 0, 1 и 5, были выделены внутри функции substr с помощью malloc,так что вы можете и должны освобождать их с помощью free.

Аналогично, каждый элемент temp_struct был выделен с помощью malloc, и поэтому можно и нужно освобождать его с помощью free.

Я не уверен, откуда вы думаете, откуда взялась бы двойная свобода.Думаете ли вы, что при вызове free(tmp_struct[i]) память, на которую указывает tmp_struct[i]->prod, также будет освобождена?Это не тот случай.Когда вы освобождаете указатель на структуру, которая содержит указатели, память для самих указателей структуры освобождается (поскольку она является частью структуры), но память, указанная на эти указатели , неи должен быть освобожден отдельно (так как он является внешним по отношению к структуре).Помимо ошибки в if условии, как вы ее написали, это правильный способ сделать это.

...