Справка по слиянию функций - PullRequest
0 голосов
/ 01 декабря 2009

У меня есть две функции:

void free_this(THIS *this)
{
    THIS *this_tmp;
    while (this_tmp = this)
    {
        if (this->str)
            free(this->str);
        this = this_tmp->next;
        free(this_tmp);
    }
}

void free_that(THAT *that)
{
    THAT *that_tmp;
    while (that_tmp = that)
    {
        if (that->id)
            free(that->id);
        that = that_tmp->next;
        free(that_tmp);
    }
}

Поскольку они очень похожи, я пытался придумать одну функцию, чтобы справиться с ними обоими. Я уже могу просто использовать указатель, чтобы указать на правильные данные для освобождения (т.е. указать либо str из этой структуры, либо id этой структуры), однако я не могу понять, как обойти, с каким типом структуры имеют дело поскольку я не могу просто использовать указатель void, поскольку в void * нет члена с именем NEXT.

Есть идеи?

Может быть, я должен как-то объединить две структуры ЭТО и ЭТО в одну? вот они:

typedef struct this {
    struct this *next;
    char *str;
} THIS;

typedef struct that {
    struct that *next;
    char *id;
    unsigned short result;
    OTHERTHING *optr;
} THAT;

Могу ли я как-нибудь использовать функцию offsetof для получения следующего элемента?

Ответы [ 5 ]

2 голосов
/ 01 декабря 2009

Вы можете реализовать функцию free с помощью void * и смещений полей. Непроверенные:

void free_either(void *either, size_t other_offset, size_t next_offset)
{
    void *either_tmp;
    while (either_tmp = either)
    {
        free((char *)either + other_offset);

        either_tmp = (char *)either + next_offset;
        free(either);
    }
}

free_either(this,offsetof(THIS,str),offsetof(THIS,next));
free_either(that,offsetof(THAT,id),offsetof(THAT,next));

Затем можно создать макросы для замены старых функций free_this или free_that.

1 голос
/ 01 декабря 2009

Еще один путь - через примитивное наследование:

struct node {
   struct node *next;
}

struct this {
   struct node mynode;
   ...
}

struct that {
   struct node mynode;
   ...
}

free_any(struct node *this)
{
    struct node *this_tmp;
    while (this_tmp = this)
    {
        this = this_tmp->next;
        free(this_tmp);
    }
}

Это работает только в том случае, если «узел» находится наверху структур, и позволяет вам только пропустить один связанный список через эти структуры.

Кроме того, это не позволяет вам освобождать что-либо, специфичное для этого типа структуры ; чтобы сделать это, вам нужно настроить функцию обратного вызова (передав ее в свободной или в некоторой управляющей структуре), которая будет вызываться. Я бы, вероятно, вместо этого реализовал бы функцию «pop», которая удаляет элемент из списка, и чтобы освободить весь список, я бы вытолкнул каждый элемент и затем освободил бы их при необходимости.

1 голос
/ 01 декабря 2009

Здесь у вас есть два разных односвязных типа списка. Вы можете обойти это, создав только один тип:

typedef struct node {
    struct node *next;
    void *data;
} NODE;

и data указывают либо на char* (или просто char), либо на другую структуру с тремя полями данных из THAT. Конечно, вы должны запомнить free() данные в вашей free_node() функции.

1 голос
/ 01 декабря 2009

Зависит от точной структуры ЭТОГО и ЭТОГО. Если они очень похожи, особенно если str и id имеют одинаковые смещения, вы можете объединить их в один объект.

structure THIS {
    void* str;
    ...
};

structure THIS {
    void* id;     /* is at the same offset as str */
    ...
};

union THAS {
    structure THIS this;
    structure THAT that;
    void* pointer; /* at the same offset as str and id */
};

/* and use it like */
void free_thas(THAS* thas) {
    free(thas->pointer);
    ...
}

Если у вас плохое предчувствие по этому поводу, вы правы. Небольшое изменение в ЭТОМ может привести к взрыву ЭТОГО и так далее. Не делай этого.

0 голосов
/ 01 декабря 2009

Есть более причудливые способы сделать то, что вы хотите - но следующего примера будет достаточно.

void free_that(void *mem, int type)
{
    switch(type) {
      case THIS_FLAG: {
        THIS *this = (THIS*)mem;

        for(this; this->str != NULL; this = this->next)
           (void)free(this->str);

        break;
      }

      case THAT_FLAG: {
        THAT *that = (THAT*)mem;

        for(that; that->id != NULL; that = that->next)
           (void)free(that->id);
      }

      default: {
        (void)free(mem);
      }
    }

  return;
}

Более причудливый способ - добавить void *mem в качестве первого элемента в структуре и назначить str и id в качестве указателей, указывающих на mem (где вы размещаете память). Это позволяет вам всегда освобождать элемент mem или смещение нулевого броска до void*.

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