Жертва выражения намерения для управления памятью - PullRequest
4 голосов
/ 05 ноября 2011

Я довольно новичок в программировании на C, и подобные вещи постоянно появляются. В качестве простого примера предположим, что у меня есть struct http_header с некоторыми char указателями:

struct http_header {
    char* name;
    char* value;
};

Я хочу заполнить http_header, где value - это строковое представление типа int. Я "чувствую", что семантически я должен быть в состоянии написать функцию, которая принимает пустой указатель заголовка, строку имени и int и заполняет заголовок соответствующим образом.

void fill_header(struct http_header *h, char* name, int value)
{
    h->name = name;
    char *value_str = malloc(100);
    sprintf(value_str, "%d", value);
    h->value = value_str;
}

int main(int argc, const char * argv[])
{
    struct http_header h;
    char *name = "Header Name";
    int val = 42;
    fill_header(&h, name, val);
    ...
    free(h.value);
}

Здесь вызывающий код читается точно так же, как и мои намерения, но в этом случае я создаю строку value динамически, что означает, что мне придется ее позже освобождать. Это не пахнет мне; кажется, что вызывающая сторона тогда слишком много знает о реализации fill_header. А в реальных реализациях может быть не так просто узнать, что нужно освободить: рассмотрите возможность заполнения массива http_header s, где только один из них должен иметь свое значение malloc ed.

Чтобы обойти это, мне нужно заранее создать строку:

void fill_header2(struct http_header *h, char* name, char *value_str)
{
    h->name = name;
    h->value = value_str;
}

int main(int argc, const char * argv[])
{
    struct http_header h;
    char *name = "Header Name";
    int value = 42;

    char value_str[100];
    sprintf(value_str, "%d", value);
    fill_header2(&h, name, value_str);
}

Поскольку этот паттерн продолжается по цепочке структур с указателями на другие структуры, я заканчиваю тем, что выполняю так много работы с функциями верхнего уровня, что функции нижнего уровня вряд ли стоят этого. Кроме того, я, по сути, пожертвовал идеей «заполнить заголовок int», которую я намеревался написать в первую очередь. Я что-то здесь упускаю? Есть ли какой-нибудь шаблон или дизайн, который облегчит мою жизнь и , чтобы мои вызовы функций отражали мои намерения?

P.S. Спасибо всем в Stackoverfow за то, что я был лучшим профессором, которого я когда-либо имел.

Ответы [ 4 ]

3 голосов
/ 05 ноября 2011

Ну, я бы пошел с первым подходом (с поворотом), а также предоставил бы функцию destroy:

struct http_header *make_header(char *name, int value)
{
    struct http_header *h = malloc(sizeof *h);
    /* ... */
    return h;
}

void destroy_header(struct http_header *h)
{
    free(h->name);
    free(h);
}

Таким образом, абонент не должен ничего знать о http_header.

Вы также можете получить версию, которая оставляет основное размещение (саму структуру) вызывающей стороне и выполняет собственное внутреннее размещение. Тогда вам нужно будет предоставить clear_header, который освобождает только выделенные fill. Но это clear_header оставляет вас с частично действительным объектом.

0 голосов
/ 05 ноября 2011

Общее решение вашей проблемы - это владение объектом, как предлагали другие.Однако простейшим решением вашей конкретной проблемы является использование массива char для value, то есть char value[12].2 ^ 32 имеет 10 десятичных цифр, +1 для знака, +1 для нулевого терминатора.

Вы должны убедиться, что 1) int не больше 32-битных во время компиляции, 2) убедитесь, чточто значение находится в некотором приемлемом диапазоне (коды HTTP имеют только 3 цифры) перед вызовом sprintf, 3) используйте snprintf.

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

0 голосов
/ 05 ноября 2011

Если вы новичок в программировании на C, возможно, вы захотите использовать консервативный сборщик мусора Boehm . GC от Boehm очень хорошо работает на практике, и систематически используя его в своем собственном коде, вы можете использовать GC_malloc вместо malloc и никогда не беспокоиться о вызове free или GC_free.

Охота на утечки памяти в коде C (или даже C ++) часто является головной болью. Существуют инструменты (например, valgrind ), которые могут вам помочь, но вы можете решить не беспокоиться об этом с помощью ГК Boehm.

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

0 голосов
/ 05 ноября 2011

Я думаю, что ваша проблема просто в том, что вы программируете асимметрично.Вы должны раз и навсегда решить, кто несет ответственность за строку внутри вашей структуры.Тогда у вас должно быть две функции, а не только одна, которая должна называться как-то вроде header_init и header_destroy.

Для функции init я бы был немного более осторожен.Проверьте аргумент 0 вашего указателя и полностью инициализируйте DS, что-то вроде *h = (http_header){ .name = name }.Вы никогда не знаете, будете ли вы или кто-то в конечном итоге добавлять другое поле в вашу структуру.Таким образом, по крайней мере все остальные поля инициализируются с 0.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...