Опять goto :)
От: ansi  
Дата: 14.01.06 08:04
Оценка: +1 :)
Довольно часто приходится захватывать некоторые ресурсы по принципу "все, либо ничего". Например, необходимо выделить память под структурку, а потом еще и для ее членов, далее открыть файл и записать его хэндл в эту структуру и в конце концов вернуть указатель на эту самую структуру. Но если что-то упало, то необходимо освободить все ранее захваченное и вернуть 0.

Пример с выделением памяти:
typedef struct
{
   char    *url;
   char    *tag;
   char    *last_modified;
   wchar_t *tmp_name;
} dl_record_t;


Без goto приходится писать нечто вроде:
static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    dl_record_t *record = (dl_record_t *)malloc(sizeof(*record));

    if (record)
    {
        record->url = (char *)malloc(url_len + 1);

        if (!record->url)
        {
            free(record);
            record = 0;
        }
        else
        {
            record->tag = (char *)malloc(tag_len + 1);

            if (!record->tag)
            {
                free(record->url);
                free(record);
                record = 0;
            }
            else
            {
                record->last_modified = (char *)malloc(last_modified_len + 1);

                if (!record->last_modified)
                {
                    free(record->url);
                    free(record->tag);
                    free(record);
                    record = 0;
                }
                else
                {
                    record->tmp_name = (wchar_t *)malloc( (tmp_name_len + 1) * sizeof(wchar_t) );

                    if (record->file_name == SYN_NULL)
                    {
                        free(record->url);
                        free(record->tag);
                        free(record->last_modified);
                        free(record);
                        record = 0;
                    }
                }
            }
        }
    }

    return record;
}


Или так (но peer review это скорее всего не пройдет):
static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    int err_code = 0;
    dl_record_t *record = (dl_record_t *)malloc(sizeof(*record));

    if (record)
    {
        ++err_code; // 1
        record->url = (char *)malloc(url_len + 1);

        if (record->url)
        {
            ++err_code; // 2
            record->tag = (char *)malloc(tag_len + 1);

            if (record->tag)
            {
                ++err_code; // 3
                record->last_modified = (char *)malloc(last_modified_len + 1);

                if (record->last_modified)
                {
                    ++err_code; // 4
                    record->tmp_name = (wchar_t *)malloc( (tmp_name_len + 1) * sizeof(wchar_t) );

                    if (record->file_name)
                    {
                        ++err_code; // 5
                    }
                }
             }
        }
    }

    switch(err_code)
    {
        case 4: free(record->last_modified);
        case 3: free(record->tag);
        case 2: free(record->url);
        case 1: free(record);
                record = 0;
    }

    return record;
}


Или еще такой вариант:
static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    int status = 0;
    dl_record_t *record = (dl_record_t *)malloc(sizeof(*record));

    if (record)
    {
        memset(record, 0, sizeof(*record));
        status = (record->url = (char *)malloc(url_len + 1)) != 0;
    }

    if (status)
    {
        status = (record->tag = (char *)malloc(tag_len + 1)) != 0;
    }

    if (status)
    {
        status = (record->last_modified = (char *)malloc(last_modified_len + 1)) != 0;
    }

    if (status)
    {
        status = (record->tmp_name = (wchar_t *)malloc( (tmp_name_len + 1) * sizeof(wchar_t) )) != 0;
    }

    if (!status && record)
    {
        if (record->last_modified)
        {
            free(record->last_modified);
        }

        if (record->tag)
        {
            free(record->tag);
        }

        if (record->url)
        {
            free(record->url);
        }

        free(record);
        record = 0;
    }

    return record;
}


Иногда делают и так:
static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    int status = 0;
    dl_record_t *record;

    while (1)
    {
        record = (dl_record_t *)malloc(sizeof(*record));
        if (!record)
        {
            break;
        }

        memset(record, 0, sizeof(*record));
        record->url = (char *)malloc(url_len + 1);
        if (!record->url)
        {
            break;
        }

        record->tag = (char *)malloc(tag_len + 1);
        if (!record->tag)
        {
            break;
        }

        record->last_modified = (char *)malloc(last_modified_len + 1);
        if (!record->last_modified)
        {
            break;
        }

        record->tmp_name = (wchar_t *)malloc( (tmp_name_len + 1) * sizeof(wchar_t) );
        if (!record->tmp_name)
        {
            break;
        }

        status = 1;
        break;
    }

    if (!status && record)
    {
        if (record->last_modified)
        {
            free(record->last_modified);
        }

        if (record->tag)
        {
            free(record->tag);
        }

        if (record->url)
        {
            free(record->url);
        }

        free(record);
        record = 0;
    }

    return record;
}


А вот так выглядит вариант с goto:
static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    dl_record_t *record = (dl_record_t *)malloc(sizeof(*record));

    if (!record)
    {
        return 0;
    }

    if (!(record->url = (char *)malloc(url_len + 1)))
    {
        goto fail_record_url;
    }

    if (!(record->tag = (char *)malloc(tag_len + 1)))
    {
        goto fail_tag;
    }

    if (!(record->last_modified = (char *)malloc(last_modified_len + 1)))
    {
        goto fail_last_modified;
    }

    if (!(record->tmp_name = (wchar_t *)malloc( (tmp_name_len + 1) * sizeof(wchar_t) )))
    {
        goto fail_tmp_name;
    }

    return record;

    fail_tmp_name:      free(record->last_modified);
    fail_last_modified: free(record->tag);
    fail_tag:           free(record->url);
    fail_record_url:    free(record);

    return 0;
}


На мой взгляд, вариант с goto более естественен и с точки зрения логики, и с точки зрения производительности. Это касается языка C. В C++ ситуация получше: статусная переменная, хелпер классы (конструктор — захват ресурса, деструктор — освобождение в случае плохого значения статусной переменной) и исключения.

Но все дело в том, что лично мне писать приходится на C. Какие еще могут быть варианты проведения отката?

25.01.06 07:41: Перенесено модератором из 'Философия программирования' — VladD2
Re: Опять goto :)
От: g_i  
Дата: 14.01.06 08:28
Оценка: 16 (3)
Здравствуйте, ansi, Вы писали:

А как-нить так не проще?


void free_record(dl_record_t** record)
{
  if (record)
  {
    free((*record)->url);
    free((*record)->tag);
    free((*record)->last_modified);
    free(*record);
    *record = 0;
  }
  return record;
}

dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    dl_record_t *record = 0; //(dl_record_t *)calloc(0, sizeof(*record));

    if (
      !(record = (dl_record_t *)calloc(0, sizeof(*record))) ||
      !(record->url = (char *)calloc(0, url_len + 1))) ||
      !(record->tag = (char *)calloc(0, tag_len + 1)) ||
      !(record->last_modified = (char *)calloc(0, last_modified_len + 1)) ||
       (record->tmp_name = (wchar_t *)calloc(0, (tmp_name_len + 1) * sizeof(wchar_t))) == SYN_NULL
       )
      free_record(&record);

    return record;
}


Re: Опять goto :)
От: Zdreni Украина http://r7.org.ru
Дата: 14.01.06 08:35
Оценка: :)
Здравствуйте, ansi, Вы писали:

A>Но все дело в том, что лично мне писать приходится на C. Какие еще могут быть варианты проведения отката?


Единственный нормальный, IMHO — каркас обработки ошибок и освобождения ресурсов, реализованный при помощи setjmp()/longjmp().
Re[2]: Опять goto :)
От: Quintanar Россия  
Дата: 14.01.06 10:46
Оценка: +1
Здравствуйте, g_i, Вы писали:

g_i>Здравствуйте, ansi, Вы писали:


g_i>А как-нить так не проще?



Нет, потому что у тебя здесь сразу два допущения.
1) То, что ресурсы можно безопасно освободить, не зная были ли они получены
2) Что выделения ресурсов идут подряд и их можно впехнуть в if

Так не бывает (с)

В С goto для таких случаев рулит, с этим ничего не поделаешь.
Re[3]: Опять goto :)
От: MShura  
Дата: 14.01.06 11:05
Оценка:
Q>Нет, потому что у тебя здесь сразу два допущения.
Единственное допущение это то, что NULL представлятся в виде (void*)0
Во всем остальном функции логически идентичены.


Q>В С goto для таких случаев рулит, с этим ничего не поделаешь.

Он рулит, но не для таких случаев.
Re[3]: Опять goto :)
От: g_i  
Дата: 14.01.06 11:39
Оценка:
Здравствуйте, Quintanar, Вы писали:


Q>Нет, потому что у тебя здесь сразу два допущения.

Q>1) То, что ресурсы можно безопасно освободить, не зная были ли они получены
Q>2) Что выделения ресурсов идут подряд и их можно впехнуть в if

Q>Так не бывает (с)


Q>В С goto для таких случаев рулит, с этим ничего не поделаешь.


Из кода, приведенного в примере следуют эти самые допущения.
Согласен, что в общем случае все не так просто. А частный, наверное, не интересен.
Re: Опять goto :)
От: Владек Россия Github
Дата: 14.01.06 15:11
Оценка: 15 (1)
Здравствуйте, ansi, Вы писали:

A>Довольно часто приходится захватывать некоторые ресурсы по принципу "все, либо ничего". Например, необходимо выделить память под структурку, а потом еще и для ее членов, далее открыть файл и записать его хэндл в эту структуру и в конце концов вернуть указатель на эту самую структуру. Но если что-то упало, то необходимо освободить все ранее захваченное и вернуть 0.


A>Пример с выделением памяти:

A>
A>typedef struct
A>{
A>   char    *url;
A>   char    *tag;
A>   char    *last_modified;
A>   wchar_t *tmp_name;
A>} dl_record_t;
A>


A>Но все дело в том, что лично мне писать приходится на C. Какие еще могут быть варианты проведения отката?


Исправил ваш первый вариант (каждый этап отката не дублируется и никаких goto)
static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    dl_record_t *record = (dl_record_t *)malloc(sizeof(*record));

    if (record != NULL)
    {
        record->url = (char *)malloc(url_len + 1);

        if (record->url != NULL)
        {
            record->tag = (char *)malloc(tag_len + 1);

            if (record->tag != NULL)
            {
                record->last_modified = (char *)malloc(last_modified_len + 1);

                if (record->last_modified != NULL)
                {
                    record->tmp_name = (wchar_t *)malloc( (tmp_name_len + 1) * sizeof(wchar_t) );

                    if (record->tmp_name != SYN_NULL)
                    {
                        return record;
                    }
                    free(record->last_modified);
                }
                free(record->tag);
            }
            free(record->url);
        }
        free(record);
    }

    return NULL;
}


Кстати, пишите на С, а проверки NULL-checks используете из C++! Нехорошо, товарищ!
... << RSDN@Home 1.1.4 stable rev. 510>>
Re[2]: Опять goto :)
От: Cyberax Марс  
Дата: 14.01.06 17:55
Оценка: +3
Владек wrote:
> Исправил ваш первый вариант (каждый этап отката не дублируется и никаких
> goto)
Ууууххх. Такое же читать невозможно. Особенно если еще пара уровней
вложенности будет.
Posted via RSDN NNTP Server 2.0
Sapienti sat!
Re[3]: Опять goto :)
От: Владек Россия Github
Дата: 14.01.06 18:13
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Владек wrote:

>> Исправил ваш первый вариант (каждый этап отката не дублируется и никаких
>> goto)
C>Ууууххх. Такое же читать невозможно. Особенно если еще пара уровней
C>вложенности будет.

Ну тогда обнулять структуру перед аллокацией полей и чуть-что сразу вызывать dealloc_всех_полей_и_структуры() и return NULL. Не нравится мне это дублирование вызовов просто.
Игра в городки развивает силу рук и меткость глаз. (С) Годзилла
Re: Опять goto :)
От: VladD2 Российская Империя www.nemerle.org
Дата: 14.01.06 18:31
Оценка: +5 -1
Здравствуйте, ansi, Вы писали:

A>На мой взгляд, вариант с goto более естественен и с точки зрения логики, и с точки зрения производительности. Это касается языка C. В C++ ситуация получше: статусная переменная, хелпер классы (конструктор — захват ресурса, деструктор — освобождение в случае плохого значения статусной переменной) и исключения.


А на мой взгляд этот код весь чистейший бред. Вот как бы это написал бы я:
static dl_record_t * alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    dl_record_t *record = (dl_record_t*)calloc(sizeof(dl_record_t));

    if (!record)
        return NULL;
        
    record->url = (char*)malloc(url_len + 1);
    record->tag = (char*)malloc(tag_len + 1);
    record->last_modified = (char*)malloc(last_modified_len + 1);
    record->tmp_name = (wchar_t*)malloc((tmp_name_len + 1) * sizeof(wchar_t));

    // Если не удалось занять память хотья бы под одну структуру данных...
    if (!(record->url && record->tag && record->last_modified && record->tmp_name))
    {
        // Освобождаем все к чертям
        free(record->url); // если free() передать NULL, то она просто ничего не сделает.
        free(record->tag);
        free(record->last_modified);
        free(record);
        return NULL;    
    }

    return record;
}


Теперь поясню почему все так, а не иначе.

Дело в том, что ситуация в которой память не выделится крайне редка. Это уже исключительная ситуация. В 99% случаев память будет выделна и проблем не будт.

Так что заниматься такой мудреной обработкой ошибок, каждый раз тратя время на тучу ненужных проверок просто глупо.
Важно, чтобы код работал быстро и эффективно в тех самых 99% случаев. А вот в нешатной ситуации можно и произвести несколько лишних вызовов.
Так же важно, чтобы код связанный с основной логикой работы приложения был компактным и понятным. Вынесение проверок успешности в концец, а то и в отдельную функцию очень способствует этому.

Ну, и главное! Казалось бы... причем тут goto?
... << RSDN@Home 1.2.0 alpha rev. 628>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Опять goto :)
От: Vovka_R Россия  
Дата: 14.01.06 19:27
Оценка: :))
Здравствуйте, ansi, Вы писали:

А если так:

static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    int cnt = 0;
    dl_record_t *record;

    while (1)
    {
        if ( !(record = (dl_record_t *)malloc(sizeof(*record))) )
        {
            cnt = 1;
            break;
        }

        memset(record, 0, sizeof(*record));
        
        if ( !(record->url = (char *)malloc(url_len + 1)) )
        {
            cnt = 2;
            break;
        }
        
        if ( !(record->tag = (char *)malloc(tag_len + 1)) )
        {
            cnt = 3;
            break;
        }
        
        if ( !(record->last_modified = (char *)malloc(last_modified_len + 1)) )
        {
            cnt = 4;
            break;
        }
        
        if ( !(record->tmp_name = (wchar_t *)malloc( (tmp_name_len + 1) * sizeof(wchar_t) )) )
        {
            cnt = 5;
            break;
        }

        break;
    }

    switch(cnt)
    {
        case 5: free(record->last_modified);
        case 4: free(record->tag);
        case 3: free(record->url);
        case 2: free(record); record = NULL;
    }

    return record;
}
Re: Опять goto :)
От: alexeiz  
Дата: 14.01.06 21:18
Оценка: +1 :)
A> dl_record_t *record = (dl_record_t *)malloc(sizeof(*record));

Замашки у вас С++сные, товарищи Сишники.
Re: Опять goto :)
От: c-smile Канада http://terrainformatica.com
Дата: 15.01.06 06:43
Оценка: 2 (1)
Здравствуйте, ansi, Вы писали:

A>Довольно часто приходится захватывать некоторые ресурсы по принципу "все, либо ничего". Например, необходимо выделить память под структурку, а потом еще и для ее членов, далее открыть файл и записать его хэндл в эту структуру и в конце концов вернуть указатель на эту самую структуру. Но если что-то упало, то необходимо освободить все ранее захваченное и вернуть 0.


A>Пример с выделением памяти:


Если уже на то по пошло то на C это пишется так:

typedef struct
{
   char    *url;
   char    *tag;
   char    *last_modified;
   wchar_t *tmp_name;
} dl_record_t;


static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
{
    dl_record_t *record = (dl_record_t *)malloc(sizeof(dl_record_t) + 
      url_len + 1 + tag_len + 1 + last_modified_len + 1+ (tmp_name_len + 1) * sizeof(wchar_t);

    if(record)
    {
      record->url = ...;
      record->tag = ...;
      record->last_modified = ...;
      record->tmp_name =...;
    }

 );
 return record;
}


А еще есть такие штуки как memory pool из APR.
Re[2]: Опять goto :)
От: ansi  
Дата: 15.01.06 10:34
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Если уже на то по пошло то на C это пишется так:


CS>
CS>static dl_record_t *
CS>alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
CS>{
CS>    dl_record_t *record = (dl_record_t *)malloc(sizeof(dl_record_t) + 
CS>      url_len + 1 + tag_len + 1 + last_modified_len + 1+ (tmp_name_len + 1) * sizeof(wchar_t);

CS>    if(record)
CS>    {
CS>      record->url = ...;
CS>      record->tag = ...;
CS>      record->last_modified = ...;
CS>      record->tmp_name =...;
CS>    }

CS> );
CS> return record;
CS>}
CS>


Ты знаешь, именно так я и написал сначала Но! Проц не интеловский (арм, по-моему). Чтение 16 бит по нечетному адресу...и падает весь телефон Возиться с выравниванием не хотелось. Да и вообще, память там разбита на блоки фиксированного размера (начиная с кучи блоков по 4байт, ..., и заканчивая парой блоков по 64Кб), причем блоки не объединяются. Так что вероятность не получить один большой блок больше, чем вероятность не получить несколько блоков размером поменьше.

CS>А еще есть такие штуки как memory pool из APR.
Re[2]: Опять goto :)
От: ansi  
Дата: 15.01.06 10:52
Оценка:
Здравствуйте, VladD2, Вы писали:

A>>На мой взгляд, вариант с goto более естественен и с точки зрения логики, и с точки зрения производительности. Это касается языка C. В C++ ситуация получше: статусная переменная, хелпер классы (конструктор — захват ресурса, деструктор — освобождение в случае плохого значения статусной переменной) и исключения.


VD>А на мой взгляд этот код весь чистейший бред. Вот как бы это написал бы я:

VD>
VD>static dl_record_t * alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
VD>{
VD>    dl_record_t *record = (dl_record_t*)calloc(sizeof(dl_record_t));

VD>    if (!record)
VD>        return NULL;
        
VD>    record->url = (char*)malloc(url_len + 1);
VD>    record->tag = (char*)malloc(tag_len + 1);
VD>    record->last_modified = (char*)malloc(last_modified_len + 1);
VD>    record->tmp_name = (wchar_t*)malloc((tmp_name_len + 1) * sizeof(wchar_t));

VD>    // Если не удалось занять память хотья бы под одну структуру данных...
VD>    if (!(record->url && record->tag && record->last_modified && record->tmp_name))
VD>    {
VD>        // Освобождаем все к чертям
VD>        free(record->url); // если free() передать NULL, то она просто ничего не сделает.
VD>        free(record->tag);
VD>        free(record->last_modified);
VD>        free(record);
VD>        return NULL;    
VD>    }

VD>    return record;
VD>}
VD>


А на мой взгляд, код q_i все-же лучше. Идея та же, но при падении одного вызова, все остальные просто не выполняются. Ты же идешь до последнего

VD>Теперь поясню почему все так, а не иначе.


VD>Дело в том, что ситуация в которой память не выделится крайне редка. Это уже исключительная ситуация. В 99% случаев память будет выделна и проблем не будт.


VD>Так что заниматься такой мудреной обработкой ошибок, каждый раз тратя время на тучу ненужных проверок просто глупо.

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

VD>Важно, чтобы код работал быстро и эффективно в тех самых 99% случаев. А вот в нешатной ситуации можно и произвести несколько лишних вызовов.

VD>Так же важно, чтобы код связанный с основной логикой работы приложения был компактным и понятным. Вынесение проверок успешности в концец, а то и в отдельную функцию очень способствует этому.
Вот серьезно, есть ли проблемы с пониманием варианта с goto?

Да, в принципе согласен и склоняюсь к этому варианту, потому как других более разумных просто нет — goto находится под строжайшим запретом! Не дай Бог выставить код с goto на инспекцию — инфаркт инспекторам обеспечен

VD>Ну, и главное! Казалось бы... причем тут goto?
Re[2]: Опять goto :)
От: ansi  
Дата: 15.01.06 11:00
Оценка:
Здравствуйте, g_i, Вы писали:

g_i>А как-нить так не проще?


Да, вариант неплохой! Но, у меня calloc в распоряжении нет, да и malloc'а тоже. Менеджер свой. Можно конечно и написать нечто вроде calloc, но уже не то (с). Поэтому аллоцирование структуры придется вынести из if'а и добавить memset. К счастью, аналог функции free освобождает нулевой указатель без проблем. Но это не со всеми ресурсами.
Замечу: вариант с goto не требует зануления структуры и проверок на выделенность ресурсов.
Re[3]: Опять goto :)
От: Anton Batenev Россия https://github.com/abbat
Дата: 15.01.06 11:36
Оценка:
Здравствуйте, ansi, Вы писали:

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


А вот меня всегда интересовал вопрос, что делать, если вызов malloc вернул NULL для объекта, который не создать нельзя? Т.е., предположим, что у нас есть два конкурирующих за память приложения, которые в процессе своей работы исчерпали всю доступную память и хотят еще ее откушать. В один из моментов, в одном из приложений, на критичном участке кода, который нельзя не выполнить, malloc вернул NULL. Что делать?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[4]: Опять goto :)
От: ansi  
Дата: 15.01.06 13:19
Оценка:
Здравствуйте, Anton Batenev, Вы писали:

AB>А вот меня всегда интересовал вопрос, что делать, если вызов malloc вернул NULL для объекта, который не создать нельзя? Т.е., предположим, что у нас есть два конкурирующих за память приложения, которые в процессе своей работы исчерпали всю доступную память и хотят еще ее откушать. В один из моментов, в одном из приложений, на критичном участке кода, который нельзя не выполнить, malloc вернул NULL. Что делать?


Очевидно, что откат, но только чтоб никто не заметил Такие ситуации довольно редки, на мой взгляд. Если отказ и будет получен, то скорее всего работоспособность можно сохранить, потому как нехватает ресурсов только для какой-то части функционала, а ронять все не надо. Но если работоспособность сохранить невозможно, то придется аккуратно (или с треском — кому как нравится) вывалиться — другого выхода просто нет...
Re[4]: Опять goto :)
От: Cyberax Марс  
Дата: 15.01.06 13:59
Оценка:
Anton Batenev wrote:
> А вот меня всегда интересовал вопрос, что делать, если вызов malloc
> вернул NULL для объекта, который *не* создать нельзя? Т.е., предположим,
> что у нас есть два конкурирующих за память приложения, которые в
> процессе своей работы исчерпали всю доступную память и хотят еще ее
> откушать. В один из моментов, в одном из приложений, на критичном
> участке кода, который нельзя не выполнить, malloc вернул NULL. Что делать?
Падать.

Как вариант — пытаться освободить кэши/временные данные и снова
повторить вызов.
Posted via RSDN NNTP Server 2.0
Sapienti sat!
Re: Опять goto :)
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 15.01.06 15:36
Оценка: 3 (1)
Здравствуйте, ansi, Вы писали:

A>Пример с выделением памяти:

A>
A>typedef struct
A>{
A>   char    *url;
A>   char    *tag;
A>   char    *last_modified;
A>   wchar_t *tmp_name;
A>} dl_record_t;
A>


Может уже предлагали такой вариант, но все же:
static dl_record_t *
destroy_record( dl_record_t * r )
  {
    if( NULL != r->url )
      free( r->url );
    if( NULL != r->tag )
      free( r->tag );
    if( NULL != r->last_modified )
      free( r->last_modified );
    if( NULL != r->tmp_name )
      free( r->tmp_name );
    free( r );
    return 0;
  }

static dl_record_t *
alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
  {
    dl_record_t * record = (dl_record_t *) malloc( sizeof( dl_record_t ) );
    if( record )
      {
        record->url_len = NULL;
        record->tag = NULL;
        record->last_modified = NULL;
        record->tmp_name = NULL;

        if( NULL == ( record->url_len = (char *)malloc( url_len + 1 ) ) )
          return destroy_record( record );
        if( NULL == ( record->tag = (char *)malloc( tag_len + 1 ) ) )
          return destroy_record( record );
        if( NULL == ( record->last_modified = (char *)malloc( last_modified_len + 1 ) ) )
          return destroy_record( record );
        if( NULL == ( record->tmp_name = (wchar_t *)malloc( sizeof( wchar_t ) * ( tmp_name_len + 1 ) ) ) )
          return destroy_record( record );
      }

    return record;
  }


A>А вот так выглядит вариант с goto:

A>
A>static dl_record_t *
A>alloc_record(int url_len, int tag_len, int last_modified_len, int tmp_name_len)
A>{
A>    dl_record_t *record = (dl_record_t *)malloc(sizeof(*record));

...

A>    fail_tmp_name:      free(record->last_modified);
A>    fail_last_modified: free(record->tag);
A>    fail_tag:           free(record->url);
A>    fail_record_url:    free(record);

A>    return 0;
A>}
A>


Этот вариант мне не нравится тем, что две не сильно связанные по логике части alloc_record (аллокация и деаллокация) оказываются сильно связанны порядком действий. Стоит только кому-то переместить выделение памяти для tmp_name в начало секции аллокации и оставить неизменной секцию деаллокации, как весь механизм рушится. А такое перемещение вполне возможно. Например, если мы знаем, что больше всего памяти требуется именно для tmp_name, то лучше попробовать выделить эту память вначале и, только если это удалось, выделять память под остальные поля.


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.