2013-07-23 2 views
3

В C функции могут возвращать указатели в память, что эта функция динамически распределена и требует вызова кода для ее освобождения. Также часто требуется, чтобы вызывающий код поставляет буфер ко второй функции, которая затем устанавливает содержимое этого буфера. Например:Возвращает указатель на динамически распределенную структуру vs. требует выделенной памяти от вызывающей функции?

struct mystruct { 
    int a; 
    char *b; 
}; 


struct mystruct *get_a_struct(int a, char*b) 
{ 
    struct mystruct *m = malloc(sizeof(struct mystruct)); 
    m->a = a; 
    m->b = b; 

    return m; 
} 

int init_a_struct(int a, char*b, struct mystruct *s) 
{ 
    int success = 0; 
    if (a < 10) { 
     success = 1; 
     s->a = a; 
     s->b = b; 
    } 

    return success; 
} 

Возможно ли такое или иное использование? Я могу думать о аргументах для обоих: для метода get_a_struct код вызова упрощается, поскольку ему нужно только free() возвращенная структура; для метода init_a_struct существует очень низкая вероятность того, что вызывающий код не будет free() динамически распределенной памяти, поскольку сам вызывающий код, вероятно, выделил его.

+0

Вы не можете бесплатно() структурировать, выделенную .dll. (По крайней мере, я не думаю, что вы можете.) Итак, если вы возитесь с dll, будьте осторожны. – Jiminion

+0

init_a_struct может быть гораздо более эффективным, так как структура может быть повторно использована. –

+0

@Jim Затем вы получите .dll функцию, которая снова освобождает данную структуру. – glglgl

ответ

1

Я думаю, что предоставление уже выделенной структуры в качестве аргумента предпочтительнее, потому что в большинстве случаев вам не нужно будет вызывать malloc/calloc в вызывающем коде и, следовательно, беспокоиться о его освобождении. Пример:

int init_struct(struct some_struct *ss, args...) 
{ 
    // init ss 
} 

int main() 
{ 
    struct some_struct foo; 
    init_struct(&foo, some_args...); 
    // free is not needed 
} 
1

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

Обратите внимание, что «не вызывать malloc» всегда является предпочтительным решением, если это возможно. Не только вызов malloc занимает некоторое время, это также означает, что в каком-то месте вам придется вызывать free в выделенной памяти, а каждый выделенный объект занимает несколько байт (обычно 12-40 байт) «накладных расходов», поэтому выделение пространства для небольших объектов, безусловно, расточительно.

+0

Спасибо за ответ. В течение долгого времени я программировал, не используя динамическое распределение памяти, но в последнее время все чаще использовал его для того, что я воспринимаю как его гибкость. – Nate

+1

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

3

Это зависит от конкретной ситуации, но в общем случае предоставление выделенного буфера кажется предпочтительнее.

Как упоминал Джим, DLL может вызывать проблемы, если вызываемая функция выделяет память. Это будет иметь место, если вы решите распространять код как Dll, и get_a_struct экспортируется в /, видимым для пользователей DLL. Затем пользователи должны выяснить, надеюсь, из документации, если они должны освободить память с помощью бесплатной, удаленной или другой конкретной функции ОС. Более того, даже если они используют правильную функцию для освобождения памяти, они могут использовать другую версию среды выполнения C/C++. Это может привести к ошибкам, которые довольно сложно найти. Проверьте this Raymond Chen или найдите «границы dll для распределения памяти». Типичным решением является экспорт из DLL вашей собственной бесплатной функции. Итак, у вас будет пара: get_a_struct/release_a_struct.

С другой стороны, иногда только вызываемая функция знает объем памяти, который необходимо выделить. В этом случае для вызываемой функции более целесообразно выполнить выделение. Если это невозможно, скажем, из-за проблемы с границей DLL, типичным, хотя и уродливым решением, является механизм для поиска этой информации. Например, в Windows функция GetCurrentDirectory вернет необходимый размер буфера, если вы передадите 0 и NULL в качестве параметров.

0

Я согласен с другими ответами, что передача выделенного struct является предпочтительной, но есть одна ситуация, когда возвращение указателя может быть предпочтительным.

Если вам нужно явно освободить какой-либо ресурс в конце (закройте файл или сокет или освободите внутреннюю часть памяти для структуры или присоедините к ней поток или что-то еще, что потребует деструктора в C++), я подумайте, что лучше выделить внутренне, а затем вернуть указатель.

Я думаю, что это так, потому что в C это означает какой-то контракт: если вы выделите свой собственный struct, вам не нужно ничего делать, чтобы его уничтожить, и он автоматически очищается в конце функция. С другой стороны, если вы получили какой-то динамически выделенный указатель, вы вынуждены называть что-то, чтобы уничтожить его в конце, и эта функция destroy_a_struct - это то место, где вы ставите другие задачи очистки, а также free.

Смежные вопросы